home *** CD-ROM | disk | FTP | other *** search
/ TeX 1995 July / TeX CD-ROM July 1995 (Disc 1)(Walnut Creek)(1995).ISO / dviware / dvitovdu32 / src / pascal / dvitovdu.p < prev    next >
Text File  |  1991-11-10  |  81KB  |  2,282 lines

  1. PROGRAM DVItoVDU (output);
  2.  
  3. CONST version = 'This is DVItoVDU, version 3.0';
  4.  
  5. (* Author:         Andrew Trevorrow
  6.    Implementation: Pyramid Pascal
  7.  
  8.    Description:
  9.    This program allows pages from a DVI file produced by TeX to be previewed
  10.    on a VDU screen.
  11.  
  12.    Notes:
  13.  - See AAAREAD.ME for revision history.
  14.  - Debugging code is bracketed by { DEBUG } ... { GUBED }.
  15.    This code will be commented out in the final working version.
  16.  - System-dependent code is indicated by the string "SYSDEP".
  17.  - Uncertain code is indicated by the string "???".
  18.  - Unfinished code is indicated by the string "!!!".
  19.  - Goto 999 is used to emulate a RETURN (from procedure) statement.
  20.  - Goto <= 888 is used to emulate an EXIT (from loop) statement.
  21.  - The above notes are also true for all separately compiled modules.
  22. *)
  23.  
  24. #include 'globals.h';
  25. #include 'screenio.h';
  26. #include 'options.h';
  27. #include 'vdu.h';
  28. #include 'dvireader.h';
  29. #include 'fontreader.h';
  30.  
  31. (* File access routines needed in ShowHelp: *)
  32.  
  33. CONST O_RDONLY = 0;   (* SYSDEP: read-only flag for open;
  34.                          value came from /usr/include/sys/fcntl.h *)
  35.  
  36. FUNCTION open (VAR path : string; flags, mode : integer) : integer;   EXTERNAL;
  37. FUNCTION read (f : integer; VAR ch : char; n : integer) : integer;    EXTERNAL;
  38. FUNCTION close (f : integer) : integer;                               EXTERNAL;
  39.  
  40. (*******************************************************************************
  41.    DECLARATIONS FOR PROCESSING USER COMMANDS
  42.  
  43.    Most commands consist of one or two characters and can be entered
  44.    in upper or lowercase.  Multiple commands are processed in the order
  45.    given but we only update the window, if necessary, at the end.
  46.    If a bad command is encountered, any further commands are ignored.
  47.    Some commands can have parameters; they are all dimensions in terms of the
  48.    current units.  Spaces before and after commands and parameters are ignored.
  49. *)
  50.  
  51. CONST
  52.    (* Possible commands are:                                                  *)
  53.    (* i                  a positive integer; display ith DVI page             *)
  54.    TeXpage   = '[';   (* start of a TeX page specification: [i0. ... .i9]     *)
  55.    NextPage  = 'N';   (* display next DVI page, depending on direction        *)
  56.    Forwards  = '>';   (* process DVI pages in ascending order                 *)
  57.    Backwards = '<';   (* process DVI pages in descending order                *)
  58.    Window    = 'W';   (* move window's top left corner to given position      *)
  59.    Up        = 'U';   (* move window up a given amount                        *)
  60.    Down      = 'D';   (* move window down a given amount                      *)
  61.    Left      = 'L';   (* move window left a given amount                      *)
  62.    Right     = 'R';   (* move window right a given amount                     *)
  63.    Hsize     = 'H';   (* set scaledwd: window's horizontal size               *)
  64.    Vsize     = 'V';   (* set scaledht: window's vertical size                 *)
  65.    AutoView  = 'A';   (* enable/disable automatic view after page selection   *)
  66.    ZoomInOut = 'Z';   (* halve/double window dimensions                       *)
  67.    Terse     = 'T';   (* display quick and nasty chars at reference points    *)
  68.    Box       = 'B';   (* display box outlines of glyphs                       *)
  69.    Full      = 'F';   (* display all pixels in glyphs                         *)
  70.    Ic        = 'I';   (* get/show dimensions in inches                        *)
  71.    Cm        = 'C';   (* get/show dimensions in centimetres                   *)
  72.    Mm        = 'M';   (* get/show dimensions in millimetres                   *)
  73.    PcPtPx    = 'P';   (* get/show dimensions in picas/points/pixels           *)
  74.    Help      = '?';   (* display help on available commands                   *)
  75.    Show      = 'S';   (* display useful statistics                            *)
  76.    Quit      = 'Q';   (* have a guess                                         *)
  77.  
  78.    commprompt = 'Command:';
  79.  
  80. VAR
  81.    commstring : string;             (* holds user responses                   *)
  82.    commpos    : INTEGER;            (* current position in commstring         *)
  83.    commlen    : INTEGER;            (* length of commstring                   *)
  84.    command    : CHAR;               (* starting character of command          *)
  85.    ascending  : BOOLEAN;            (* initially TRUE; changed by the
  86.                                        Forwards/Backwards commands to control
  87.                                        how to get the next DVI page           *)
  88.    maxpix     : INTEGER;            (* maximum absolute pixel value;
  89.                                        depends on resolution                  *)
  90.    (* These flags are used to handle multiple commands:                       *)
  91.    screenjustcleared,               (* has screen just been cleared?          *)
  92.    paintDVIStatus,                  (* does DVI status line need updating?    *)
  93.    paintWindowStatus,               (* does window status line need updating? *)
  94.    paintwindow,                     (* does window region need updating?      *)
  95.    pageoffpaper,                    (* is page off paper?                     *)
  96.    badcommand : BOOLEAN;            (* was there a bad command?               *)
  97.  
  98.  
  99. (*******************************************************************************
  100.    DECLARATIONS FOR DISPLAYING A PAGE
  101.  
  102.    The reference points of characters and rules on a page are stored as
  103.    pairs of horizontal and vertical paper pixel coordinates.
  104.    The paper coordinate scheme is described in detail in DVIReader.
  105.    The screen coordinate scheme is described in detail in VDU.
  106.    To update the window region, DVItoVDU maps visible paper pixels
  107.    to screen pixels using windowh and windowv to help with translation,
  108.    and windowwd and windowht to help with scaling.
  109.    What the user sees depends on the current displaymode, the current size
  110.    of the window region (scaledwd by scaledht are in paper pixels and determine
  111.    the horizontal and vertical scaling factors), and the current paper position
  112.    of the window region's top left corner; i.e., (windowleft,windowtop).
  113.  
  114.    A NOTE ON THE SCALING METHOD USED BY DVItoVDU:
  115.    We desire the following conditions when scaling paper pixels to
  116.    screen pixels:
  117.    1. Rules/glyphs having the same top/bottom/left/right paper coordinates also
  118.       have the same screen coordinates (e.g., to ensure baselines line up).
  119.       This condition is incompatible with a rule/glyph staying the same
  120.       width and height as the window position changes!  Too bad.
  121.    2. After being scaled, visible pixel positions must not exceed the
  122.       window region's edges.  In our case, only the bottom and right edges are
  123.       a problem because scaling starts at the top left corner of the window.
  124.       We use two different scaling calculations depending on
  125.       whether the h/vscalefactors are < 1.0 or not.
  126.    3. Scaled heights and widths must be > 0 even when h/vscalefactors
  127.       approach 0.  If h/vscalefactors are > 1.0 then the width/height of
  128.       paper pixels increase accordingly.
  129. *)
  130.  
  131. VAR
  132.    displaymode    : (tersemode      (* show quick and nasty chars at ref pts  *)
  133.                     ,boxmode        (* show box outlines of glyphs            *)
  134.                     ,fullmode       (* show all pixels in glyphs              *)
  135.                     );
  136.    currentunits   : (inunits        (* get/show dimensions in inches          *)
  137.                     ,cmunits        (* get/show dimensions in centimetres     *)
  138.                     ,mmunits        (* get/show dimensions in millimetres     *)
  139.                     ,pcunits        (* get/show dimensions in picas           *)
  140.                     ,ptunits        (* get/show dimensions in points          *)
  141.                     ,pxunits        (* get/show dimensions in pixels          *)
  142.                     );
  143.    papertop,
  144.    paperleft,
  145.    paperbottom,
  146.    paperright     : INTEGER;        (* these define the edges of the paper    *)
  147.    windowtop,
  148.    windowleft,
  149.    windowbottom,
  150.    windowright    : INTEGER;        (* these define the current window edges  *)
  151.    allpagevisible : BOOLEAN;        (* is all of page visible in window?      *)
  152.    outsidepage    : BOOLEAN;        (* is entire window outside page?         *)
  153.    scaledht       : INTEGER;        (* current window height in paper pixels  *)
  154.    scaledwd       : INTEGER;        (* current window width in paper pixels   *)
  155.    vscalefactor   : REAL;           (* windowht / scaledht                    *)
  156.    hscalefactor   : REAL;           (* windowwd / scaledwd                    *)
  157.    thisruleinfo   : ruleinfoptr;    (* current rule info in rulelist          *)
  158.    unusedfont     : fontinfoptr;    (* first unused font in sorted fontlist   *)
  159.    thisfontinfo   : fontinfoptr;    (* current font info in sorted fontlist   *)
  160.    thischarinfo   : charinfoptr;    (* current char info in charlist          *)
  161.    thischar       : INTEGER;        (* current index into current chartable   *)
  162.    fontopen       : BOOLEAN;        (* is thisfontinfo^.fontspec open?        *)
  163.    useraborted    : BOOLEAN;        (* did user abort page display?           *)
  164.    autoviewing    : BOOLEAN;        (* automatic window view enabled?         *)
  165.  
  166. (******************************************************************************)
  167.  
  168. PROCEDURE Initialize;
  169.  
  170. BEGIN
  171. (* TeX will not generate dimensions > than about 38 feet, so we
  172.    choose an absolute limit on our dimensions to be 40 feet.
  173. *)
  174. maxpix     := 40 * 12 * resolution;
  175.  
  176. (* top left corner of paper is fixed at (-1",-1") *)
  177. papertop    := -resolution;
  178. paperleft   := -resolution;
  179. paperbottom := papertop  + paperht - 1;
  180. paperright  := paperleft + paperwd - 1;
  181.  
  182. (* User sees the following status values before requesting the first page.
  183.    Note that DVIReader has already initialized currDVIpage and currTeXpage.
  184. *)
  185. ascending := TRUE;             (* process DVI pages in ascending order *)
  186. displaymode := tersemode;
  187. windowtop := 0;                (* window location *)
  188. windowleft := 0;
  189. scaledht := windowht;          (* window size is initially unscaled *)
  190. scaledwd := windowwd;
  191. minhp := 0; minvp := 0;        (* page location *)
  192. maxhp := 0; maxvp := 0;
  193. currentunits := inunits;       (* units are initially inches *)
  194. vscalefactor := 1.0;
  195. hscalefactor := 1.0;
  196. autoviewing := TRUE;           (* start off enabled *)
  197. END; (* Initialize *)
  198.  
  199. (******************************************************************************)
  200.  
  201. PROCEDURE ClearMessageLine;
  202.  
  203. (* Clear message line and move cursor to start of line.
  204.    We don't show any message here; that will usually be done
  205.    immediately after calling this routine.
  206. *)
  207.  
  208. BEGIN
  209. ClearTextLine(messagel);
  210. MoveToTextLine(messagel);
  211. END; (* ClearMessageLine *)
  212.  
  213. (******************************************************************************)
  214.  
  215. PROCEDURE WaitForReturn;
  216.  
  217. (* DVItoVDU has just displayed an important message.
  218.    To ensure message is seen we wait for user to hit Return.
  219. *)
  220.  
  221. VAR ch : CHAR;
  222.  
  223. BEGIN
  224. WriteString('   RETURN:');
  225. WriteBuffer;
  226. REPEAT ReadChar(ch) UNTIL ch = CR;
  227. END; (* WaitForReturn *)
  228.  
  229. (******************************************************************************)
  230.  
  231. PROCEDURE UpdateDVIStatusLine;
  232.  
  233. (* Show totalpages, currDVIpage, currTeXpage, direction and displaymode. *)
  234.  
  235. LABEL 888;
  236.  
  237. VAR i, lastnonzero : INTEGER;
  238.  
  239. BEGIN
  240. ClearTextLine(DVIstatusl);
  241. MoveToTextLine(DVIstatusl);
  242. WriteString('Total pages='); WriteInt(totalpages);
  243. WriteString('   DVI page='); WriteInt(currDVIpage);
  244. WriteString('   TeX page='); WriteChar('[');
  245. lastnonzero := 9;
  246. WHILE lastnonzero > 0 DO BEGIN
  247.    IF currTeXpage[lastnonzero] <> 0 THEN goto 888;
  248.    lastnonzero := lastnonzero - 1;   (* find last counter with non-zero value *)
  249. END;
  250. 888:
  251. (* always show \count0 but don't show trailing 0 counters *)
  252. FOR i := 0 TO lastnonzero DO BEGIN
  253.    WriteInt(currTeXpage[i]);
  254.    IF i <> lastnonzero THEN WriteChar('.');
  255. END;
  256. WriteChar(']');
  257. WriteString('   Next=');
  258. IF ascending THEN WriteChar('>') ELSE WriteChar('<');
  259. WriteString('   Auto=');
  260. IF autoviewing THEN WriteChar('+') ELSE WriteChar('-');
  261. CASE displaymode OF
  262.    tersemode : WriteString('   Terse');
  263.    boxmode   : WriteString('   Box')  ;
  264.    fullmode  : WriteString('   Full')
  265. END;
  266. WriteLine;
  267. END; (* UpdateDVIStatusLine *)
  268.  
  269. (******************************************************************************)
  270.  
  271. PROCEDURE WriteDimension (pixels : INTEGER);
  272.  
  273. (* Show the given pixel dimension in terms of currentunits. *)
  274.  
  275. LABEL 999;
  276.  
  277. VAR realdim : REAL;   fracpart : INTEGER;
  278.  
  279. BEGIN
  280. CASE currentunits OF
  281.    inunits : realdim := pixels / resolution;
  282.    cmunits : realdim := pixels / resolution * 2.54;
  283.    mmunits : realdim := pixels / resolution * 25.4;
  284.    pcunits : realdim := pixels / resolution * 72.27 / 12.0;
  285.    ptunits : realdim := pixels / resolution * 72.27;
  286.    pxunits : BEGIN WriteInt(pixels); goto 999 END
  287. END;
  288. (* show realdim to an accuracy of 1 decimal place *)
  289. IF ABS(realdim) < 0.05 THEN
  290.    WriteString('0.0')
  291. ELSE BEGIN
  292.    IF realdim < 0.0 THEN BEGIN
  293.       WriteChar('-');
  294.       realdim := ABS(realdim);
  295.    END;
  296.    realdim := realdim + 0.05;     (* round up to 1 decimal place *)
  297.    WriteInt(TRUNC(realdim));      (* whole part *)
  298.    WriteChar('.');
  299.    fracpart := TRUNC((realdim - TRUNC(realdim)) * 10.0);   (* 0..9 *)
  300.    WriteInt(fracpart);
  301. END;
  302. 999:
  303. END; (* WriteDimension *)
  304.  
  305. (******************************************************************************)
  306.  
  307. PROCEDURE UpdateWindowStatusLine;
  308.  
  309. (* Show current window location and size, page location and size, and units. *)
  310.  
  311. BEGIN
  312. ClearTextLine(windowstatusl);
  313. MoveToTextLine(windowstatusl);
  314. WriteString('Window at (');                     WriteDimension(windowleft);
  315. WriteChar(',');                                 WriteDimension(windowtop);
  316. WriteChar(')'); WriteChar(' ');                 WriteDimension(scaledwd);
  317. WriteString(' by'); WriteChar(' ');             WriteDimension(scaledht);
  318. WriteString('   Page at (');                    WriteDimension(minhp);
  319. WriteChar(',');                                 WriteDimension(minvp);
  320. WriteChar(')'); WriteChar(' ');                 WriteDimension(maxhp-minhp+1);
  321. WriteString(' by'); WriteChar(' ');             WriteDimension(maxvp-minvp+1);
  322. WriteChar(' '); WriteChar(' '); WriteChar(' ');
  323. CASE currentunits OF
  324.    inunits : WriteString('IN');
  325.    cmunits : WriteString('CM');
  326.    mmunits : WriteString('MM');
  327.    pcunits : WriteString('PC');
  328.    ptunits : WriteString('PT');
  329.    pxunits : WriteString('PX')
  330. END;
  331. WriteLine;
  332. END; (* UpdateWindowStatusLine *)
  333.  
  334. (******************************************************************************)
  335.  
  336. FUNCTION GetInteger (str      : string;    (* in *)
  337.                      strlen   : INTEGER;   (* in *)
  338.                      VAR pos  : INTEGER;   (* in/out *)
  339.                      VAR n    : INTEGER    (* out *)
  340.                     ) : BOOLEAN;
  341.  
  342. (* Extract an integer from given str starting at given pos.
  343.    pos is also used to return the position after the integer.
  344.    If no integer is found then set n to 0 and return FALSE (pos will only
  345.    change if leading spaces were skipped).
  346.    If ABS(n) > limit then set n to sign * limit.
  347.    Valid syntax is  +{digit}  or  -{digit}  or  digit{digit}.
  348.    Note that a + or - by itself is valid and sets n to 0.
  349. *)
  350.  
  351. LABEL 777, 888;
  352.  
  353. CONST limit = 2147483647;          (* TeX's limit = 2^31 - 1.
  354.                                       Should also be >= maxpix.
  355.                                       Note that this also defines the range of
  356.                                       page numbers the user can ask for! *)
  357.       threshold = limit DIV 10;    (* nearing overflow *)
  358.  
  359. VAR   absval, last : INTEGER;
  360.       sign : INTEGER;
  361.       inttoobig : BOOLEAN;
  362.  
  363. BEGIN
  364. WHILE pos < strlen DO BEGIN   (* skip any spaces *)
  365.    IF str[pos] <> ' ' THEN goto 888;
  366.    pos := pos + 1;
  367. END;
  368. 888:
  369. absval := 0; sign := 1; last := pos;
  370. inttoobig := FALSE;
  371. IF pos < strlen THEN BEGIN
  372.    IF str[pos] = '-' THEN BEGIN
  373.       sign := -1;
  374.       last := last + 1;
  375.    END
  376.    ELSE
  377.       IF str[pos] = '+' THEN last := last + 1;
  378.    WHILE last < strlen DO BEGIN
  379.       IF (str[last] < '0') OR (str[last] > '9') THEN goto 777;
  380.       IF (absval > threshold) OR
  381.          ((absval = threshold) AND (str[last] > '7')) THEN
  382.          inttoobig := TRUE
  383.       ELSE
  384.          absval := absval * 10 + (ORD(str[last]) - ORD('0'));
  385.       last := last + 1;
  386.    END;
  387.    777:
  388. END;
  389. IF pos = last THEN BEGIN
  390.    n := 0;
  391.    GetInteger := FALSE;
  392. END
  393. ELSE BEGIN
  394.    pos := last;
  395.    IF inttoobig THEN absval := limit;
  396.    n := sign * absval;
  397.    GetInteger := TRUE;
  398. END;
  399. END; (* GetInteger *)
  400.  
  401. (******************************************************************************)
  402.  
  403. FUNCTION GetDimension (str     : string;    (* in *)
  404.                        strlen  : INTEGER;   (* in *)
  405.                        VAR pos : INTEGER;   (* in/out *)
  406.                        VAR n   : INTEGER    (* out *)
  407.                       ) : BOOLEAN;
  408.  
  409. (* Extract a dimension from given str starting at given pos.
  410.    n returns the corresponding number of pixels in the dimension
  411.    (which is an integer or real value in terms of currentunits);
  412.    pos is also used to return the position after the dimension.
  413.    If no dimension is found then set n to 0 and return FALSE (pos will only
  414.    change if leading spaces were skipped).
  415.    If ABS(n) > maxpix then set n to sign * maxpix.
  416.    Valid syntax of a dimension is  integer[.{digit}]  or  .{digit}  where
  417.    an integer is defined by GetInteger.
  418.    Real dimensions are truncated to 4 decimal places.
  419.    Note that a sign or decimal point by itself is valid and sets n to 0.
  420. *)
  421.  
  422. LABEL 777, 888, 999;
  423.  
  424. VAR sign, intdim : INTEGER;
  425.     fracpart, divisor : INTEGER;
  426.     absrealdim : REAL;
  427.     intpresent, dimtoobig : BOOLEAN;
  428.  
  429. BEGIN
  430. (* GetInteger does not remember a sign by itself, so we need to check
  431.    for -ve dimensions like -.5 first.
  432. *)
  433. WHILE pos < strlen DO BEGIN   (* skip any spaces *)
  434.    IF str[pos] <> ' ' THEN goto 888;
  435.    pos := pos + 1;
  436. END;
  437. 888:
  438. sign := 1;
  439. IF (pos < strlen) THEN
  440.    IF (str[pos] = '-') THEN
  441.       sign := -1;
  442. intpresent := GetInteger(str,strlen,pos,intdim);
  443. IF (NOT intpresent) THEN
  444.    IF ((pos = strlen) OR (str[pos] <> '.')) THEN BEGIN
  445.       n := 0;
  446.       GetDimension := FALSE;
  447.       goto 999;
  448.    END;
  449. (* dimension is valid; if no integer part then intdim will be 0; sign = +|-1 *)
  450. IF (pos = strlen) OR (str[pos] <> '.') THEN
  451.    (* no fractional part *)
  452.    absrealdim := ABS(intdim)
  453. ELSE BEGIN
  454.    (* extract fractional part *)
  455.    pos := pos + 1;       (* skip over decimal point *)
  456.    divisor := 1;
  457.    fracpart := 0;
  458.    WHILE pos < strlen DO BEGIN
  459.       IF (str[pos] < '0') OR (str[pos] > '9') THEN goto 777;
  460.       (* only consider up to 4 decimal places *)
  461.       IF divisor < 10000 THEN BEGIN
  462.          divisor := divisor * 10;
  463.          fracpart := fracpart * 10 + (ORD(str[pos]) - ORD('0'));
  464.       END;
  465.       pos := pos + 1;
  466.    END;
  467.    777:
  468.    absrealdim := ABS(intdim) + (fracpart / divisor);
  469. END;
  470. (* calculate n based on absrealdim, sign and currentunits *)
  471. dimtoobig := FALSE;
  472. CASE currentunits OF
  473.    inunits :
  474.       IF absrealdim > maxpix / resolution THEN
  475.          dimtoobig := TRUE
  476.       ELSE
  477.          n := sign * TRUNC(absrealdim * resolution + 0.5);
  478.    cmunits :
  479.       IF absrealdim > (maxpix / resolution) * 2.54 THEN
  480.          dimtoobig := TRUE
  481.       ELSE
  482.          n := sign * TRUNC((absrealdim / 2.54) * resolution + 0.5);
  483.    mmunits :
  484.       IF absrealdim > (maxpix / resolution) * 25.4 THEN
  485.          dimtoobig := TRUE
  486.       ELSE
  487.          n := sign * TRUNC((absrealdim / 25.4) * resolution + 0.5);
  488.    pcunits :
  489.       IF absrealdim > (maxpix / resolution) * (72.27 / 12.0) THEN
  490.          dimtoobig := TRUE
  491.       ELSE
  492.          n := sign * TRUNC((absrealdim / 72.27) * 12.0 * resolution + 0.5);
  493.    ptunits :
  494.       IF absrealdim > (maxpix / resolution) * 72.27 THEN
  495.          dimtoobig := TRUE
  496.       ELSE
  497.          n := sign * TRUNC((absrealdim / 72.27) * resolution + 0.5);
  498.    pxunits :
  499.       IF absrealdim > maxpix THEN
  500.          dimtoobig := TRUE
  501.       ELSE
  502.          n := sign * TRUNC(absrealdim + 0.5);
  503. END;
  504. IF dimtoobig THEN n := sign * maxpix;
  505. GetDimension := TRUE;
  506. 999:
  507. END; (* GetDimension *)
  508.  
  509. (******************************************************************************)
  510.  
  511. PROCEDURE BadCommandMessage;
  512.  
  513. (* A bad command has just been detected and some sort of message displayed.
  514.    Note that commpos is pointing to just after the problem character.
  515.    If there are further commands then we show user what will be ignored.
  516. *)
  517.  
  518. VAR i : INTEGER;
  519.  
  520. BEGIN
  521. badcommand := TRUE;
  522. ClearTextLine(commandl);
  523. MoveToTextLine(commandl);
  524. WriteString(commprompt);
  525. FOR i := 0 TO commpos-1 DO WriteChar(commstring[i]);
  526. WriteChar('!');                   (* put ! after the problem character *)
  527. IF commpos < commlen THEN BEGIN
  528.    WriteString('   Ignoring:');
  529.    FOR i := commpos TO commlen-1 DO WriteChar(commstring[i]);
  530. END;
  531. WaitForReturn;
  532. ClearMessageLine;
  533. ClearTextLine(commandl);
  534. END; (* BadCommandMessage *)
  535.  
  536. (******************************************************************************)
  537.  
  538. PROCEDURE NewLocation (newhp, newvp : INTEGER);
  539.  
  540. (* Change window location to given position and update window edges.
  541.    If entire window moves outside non-empty page rectangle then outsidepage
  542.    becomes TRUE and we restrict movement to just beyond the edge(s) so that
  543.    user can easily move window (via Up,Down,Left,Right) to positions
  544.    in which one or more window and page edges coincide.
  545.    Note that allpagevisible is also updated.
  546. *)
  547.  
  548. BEGIN
  549. outsidepage := FALSE;
  550. IF (currDVIpage <> 0) AND (NOT pageempty) THEN BEGIN
  551.    (* check if new position puts window entirely outside edges;
  552.       if so then minimize the movement needed to keep this true *)
  553.    IF newvp > maxvp THEN BEGIN
  554.       outsidepage := TRUE;
  555.       newvp := maxvp + 1;
  556.    END
  557.    ELSE IF newvp < (minvp - scaledht + 1) THEN BEGIN
  558.       outsidepage := TRUE;
  559.       newvp := minvp - scaledht;
  560.    END;
  561.    IF newhp > maxhp THEN BEGIN
  562.       outsidepage := TRUE;
  563.       newhp := maxhp + 1;
  564.    END
  565.    ELSE IF newhp < (minhp - scaledwd + 1) THEN BEGIN
  566.       outsidepage := TRUE;
  567.       newhp := minhp - scaledwd;
  568.    END;
  569. END;
  570. windowtop := newvp;
  571. windowleft := newhp;
  572. windowbottom := windowtop + scaledht - 1;
  573. windowright := windowleft + scaledwd - 1;
  574. allpagevisible := (currDVIpage <> 0) AND (NOT pageempty) AND
  575.                   (minvp >= windowtop) AND (maxvp <= windowbottom) AND
  576.                   (minhp >= windowleft) AND (maxhp <= windowright);
  577. (* even if pageempty or window hasn't moved we must still call DisplayPage *)
  578. IF currDVIpage <> 0 THEN paintwindow := TRUE;
  579. END; (* NewLocation *)
  580.  
  581. (******************************************************************************)
  582.  
  583. PROCEDURE WindowMove;
  584.  
  585. (* Syntax of Window command is  W hpos,vpos  where hpos and vpos are
  586.    dimensions with leading and/or trailing spaces.  If hpos,vpos absent then
  587.    we move to minhp,minvp (top left corner of page rectangle).
  588. *)
  589.  
  590. LABEL 888;
  591.  
  592. VAR hpos, vpos : INTEGER;       (* move window to this new position *)
  593.  
  594. BEGIN
  595. (* commpos is positioned after W *)
  596. IF GetDimension(commstring,commlen,commpos,hpos) THEN BEGIN
  597.    WHILE commpos < commlen DO BEGIN
  598.       IF commstring[commpos] <> ' ' THEN goto 888;
  599.       commpos := commpos + 1;   (* skip any spaces before comma *)
  600.    END;
  601.    888:
  602.    IF (commpos = commlen) OR                    (* , vpos is missing *)
  603.       (commstring[commpos] <> ',') THEN BEGIN   (* , is missing *)
  604.       ClearMessageLine;
  605.       WriteString('Comma expected!');
  606.       IF commpos < commlen THEN commpos := commpos + 1;
  607.       BadCommandMessage;
  608.    END
  609.    ELSE BEGIN
  610.       commpos := commpos + 1;   (* skip over comma *)
  611.       IF GetDimension(commstring,commlen,commpos,vpos) THEN
  612.          NewLocation(hpos,vpos)
  613.       ELSE BEGIN
  614.          ClearMessageLine;
  615.          WriteString('Vertical coordinate expected!');
  616.          IF commpos < commlen THEN commpos := commpos + 1;
  617.          BadCommandMessage;
  618.       END;
  619.    END;
  620. END
  621. ELSE
  622.    NewLocation(minhp,minvp);   (* hpos,vpos absent *)
  623. END; (* WindowMove *)
  624.  
  625. (******************************************************************************)
  626.  
  627. PROCEDURE WindowUpDown;
  628.  
  629. VAR amount : INTEGER;     (* move window up/down this many pixels *)
  630.  
  631. BEGIN
  632. (* commpos is positioned after U or D *)
  633. IF GetDimension(commstring,commlen,commpos,amount) THEN
  634.    (* do nothing *)
  635. ELSE
  636.    amount := scaledht;    (* if amount absent, set to window height *)
  637. IF command = Up THEN
  638.    amount := -amount;
  639. NewLocation(windowleft,windowtop+amount);
  640. END; (* WindowUpDown *)
  641.  
  642. (******************************************************************************)
  643.  
  644. PROCEDURE WindowLeftRight;
  645.  
  646. VAR amount : INTEGER;     (* move window left/right this many pixels *)
  647.  
  648. BEGIN
  649. (* commpos is positioned after L or R *)
  650. IF GetDimension(commstring,commlen,commpos,amount) THEN
  651.    (* do nothing *)
  652. ELSE
  653.    amount := scaledwd;    (* if amount absent, set to window width *)
  654. IF command = Left THEN
  655.    amount := -amount;
  656. NewLocation(windowleft+amount,windowtop);
  657. END; (* WindowLeftRight *)
  658.  
  659. (******************************************************************************)
  660.  
  661. PROCEDURE NewWindowWidth (wd : INTEGER);
  662.  
  663. (* Set window width to given value (> 0 and <= max dimension). *)
  664.  
  665. BEGIN
  666. scaledwd := wd;
  667. hscalefactor := windowwd / scaledwd;
  668. END; (* NewWindowWidth *)
  669.  
  670. (******************************************************************************)
  671.  
  672. PROCEDURE SetWindowWidth;
  673.  
  674. (* Set horizontal size of window region to given dimension; if <= 0 then set
  675.    horizontal size to 1 pixel.
  676.    If no parameter then use the unscaled width represented by windowwd.
  677. *)
  678.  
  679. VAR wd : INTEGER;
  680.  
  681. BEGIN
  682. (* commpos is positioned after H *)
  683. IF GetDimension(commstring,commlen,commpos,wd) THEN BEGIN
  684.    (* note that maximum value of wd is restricted to maxpix *)
  685.    IF wd <= 0 THEN wd := 1;
  686.    NewWindowWidth(wd);
  687. END
  688. ELSE
  689.    NewWindowWidth(windowwd);   (* parameter absent *)
  690. END; (* SetWindowWidth *)
  691.  
  692. (******************************************************************************)
  693.  
  694. PROCEDURE NewWindowHeight (ht : INTEGER);
  695.  
  696. (* Set window height to given value (> 0 and <= max dimension). *)
  697.  
  698. BEGIN
  699. scaledht := ht;
  700. vscalefactor := windowht / scaledht;
  701. END; (* NewWindowHeight *)
  702.  
  703. (******************************************************************************)
  704.  
  705. PROCEDURE SetWindowHeight;
  706.  
  707. (* Set vertical size of window region to given dimension; if <= 0 then set
  708.    vertical size to 1 pixel.
  709.    If no parameter then use the unscaled height represented by windowht.
  710. *)
  711.  
  712. VAR ht : INTEGER;
  713.  
  714. BEGIN
  715. (* commpos is positioned after V *)
  716. IF GetDimension(commstring,commlen,commpos,ht) THEN BEGIN
  717.    (* note that maximum value of ht is restricted to maxpix *)
  718.    IF ht <= 0 THEN ht := 1;
  719.    NewWindowHeight(ht);
  720. END
  721. ELSE
  722.    NewWindowHeight(windowht);   (* parameter absent *)
  723. END; (* SetWindowHeight *)
  724.  
  725. (******************************************************************************)
  726.  
  727. FUNCTION ScaleHpos (h : INTEGER) : INTEGER;
  728.  
  729. (* Return a scaled value for the given horizontal window coordinate. *)
  730.  
  731. BEGIN
  732. IF hscalefactor > 1.0 THEN
  733.    ScaleHpos := TRUNC ( h * hscalefactor + 0.5 )
  734. ELSE
  735.    ScaleHpos := TRUNC ( (h + 0.5) * hscalefactor );   (* hscalefactor <= 1.0 *)
  736. END; (* ScaleHpos *)
  737.  
  738. (******************************************************************************)
  739.  
  740. FUNCTION ScaleVpos (v : INTEGER) : INTEGER;
  741.  
  742. (* Return a scaled value for the given vertical window coordinate. *)
  743.  
  744. BEGIN
  745. IF vscalefactor > 1.0 THEN
  746.    ScaleVpos := TRUNC ( v * vscalefactor + 0.5 )
  747. ELSE
  748.    ScaleVpos := TRUNC ( (v + 0.5) * vscalefactor );   (* vscalefactor <= 1.0 *)
  749. END; (* ScaleVpos *)
  750.  
  751. (******************************************************************************)
  752.  
  753. PROCEDURE SetAutoView;
  754.  
  755. (* Parse the rest of an AutoView command and do it.
  756.    commpos is pointing to next position in commandstr (and should be + or -).
  757. *)
  758.  
  759. VAR nextch : CHAR;
  760.  
  761. BEGIN
  762. IF commpos < commlen THEN BEGIN
  763.    nextch := Cap(commstring[commpos]);
  764.    commpos := commpos + 1;
  765. END
  766. ELSE
  767.    nextch := ' ';
  768. IF nextch = '+' THEN
  769.    autoviewing := TRUE
  770. ELSE IF nextch = '-' THEN
  771.    autoviewing := FALSE
  772. ELSE BEGIN
  773.    ClearMessageLine;
  774.    WriteString('A+ or A- expected!');
  775.    BadCommandMessage;
  776. END;
  777. END; (* SetAutoView *)
  778.  
  779. (******************************************************************************)
  780.  
  781. PROCEDURE ZoomWindow;
  782.  
  783. (* Parse the rest of a ZoomInOut command and do it.
  784.    commpos is pointing to next position in commandstr (and should be I or O).
  785. *)
  786.  
  787. VAR nextch : CHAR;
  788.  
  789. BEGIN
  790. IF commpos < commlen THEN BEGIN
  791.    nextch := Cap(commstring[commpos]);
  792.    commpos := commpos + 1;
  793. END
  794. ELSE
  795.    nextch := ' ';
  796. IF nextch = 'I' THEN BEGIN
  797.    (* scaledwd and scaledht are > 0 *)
  798.    NewWindowWidth((scaledwd + 1) DIV 2);
  799.    NewWindowHeight((scaledht + 1) DIV 2);
  800. END
  801. ELSE IF nextch = 'O' THEN BEGIN
  802.    (* avoid overflow *)
  803.    IF maxpix DIV 2 > scaledwd THEN
  804.       NewWindowWidth(scaledwd * 2)
  805.    ELSE
  806.       NewWindowWidth(maxpix);
  807.    IF maxpix DIV 2 > scaledht THEN
  808.       NewWindowHeight(scaledht * 2)
  809.    ELSE
  810.       NewWindowHeight(maxpix);
  811. END
  812. ELSE BEGIN
  813.    ClearMessageLine;
  814.    WriteString('ZI or ZO expected!');
  815.    BadCommandMessage;
  816. END;
  817. END; (* ZoomWindow *)
  818.  
  819. (******************************************************************************)
  820.  
  821. FUNCTION NextPageFound : BOOLEAN;
  822.  
  823. (* User has selected next page in DVI file; what they get will depend on
  824.    the current DVI page and whether we are ascending or not.
  825.    Return TRUE iff we can move to next page.
  826. *)
  827.  
  828. BEGIN
  829. IF (currDVIpage = 1) AND (NOT ascending) THEN BEGIN
  830.    ClearMessageLine;
  831.    WriteString('You are looking at first DVI page!');
  832.    BadCommandMessage;
  833.    NextPageFound := FALSE;
  834. END
  835. ELSE IF (currDVIpage = totalpages) AND ascending THEN BEGIN
  836.    ClearMessageLine;
  837.    WriteString('You are looking at last DVI page!');
  838.    BadCommandMessage;
  839.    NextPageFound := FALSE;
  840. END
  841. ELSE BEGIN
  842.    MoveToNextPage(ascending);   (* position to next DVI page *)
  843.    NextPageFound := TRUE;
  844. END;
  845. END; (* NextPageFound *)
  846.  
  847. (******************************************************************************)
  848.  
  849. FUNCTION DVIPageFound (n : INTEGER) : BOOLEAN;
  850.  
  851. (* User has selected a particular DVI page number.
  852.    Move to page n and return TRUE iff n is in 1..totalpages.
  853. *)
  854.  
  855. BEGIN
  856. IF (n < 1) OR (n > totalpages) THEN BEGIN
  857.    ClearMessageLine;
  858.    IF totalpages > 1 THEN BEGIN
  859.       WriteString('You can only request DVI pages 1 to'); WriteChar(' ');
  860.       WriteInt(totalpages);   WriteChar('!');
  861.    END
  862.    ELSE
  863.       WriteString('You can only request DVI page 1!');
  864.    BadCommandMessage;
  865.    DVIPageFound := FALSE;
  866. END
  867. ELSE BEGIN
  868.    MoveToDVIPage(n);   (* position to given DVI page *)
  869.    DVIPageFound := TRUE;
  870. END;
  871. END; (* DVIPageFound *)
  872.  
  873. (******************************************************************************)
  874.  
  875. FUNCTION ParseTeXpage (VAR newTeXpage : TeXpageinfo) : BOOLEAN;
  876.  
  877. (* Return TRUE iff TeX page specification in commstring is valid.  If so then
  878.    newTeXpage will contain the appropriate information for MoveToTeXPage.
  879.    The syntax of a TeX page specification is [n{.n}] where n is any integer as
  880.    defined by GetInteger.  Up to 10 integers may be given and are separated by
  881.    periods, even if absent.  Trailing periods may be omitted.  Spaces before
  882.    and after integers and periods are skipped.  The 10 positions correspond to
  883.    the \count0, \count1, ... ,\count9 values that TeX stores with every page.
  884.    commpos is initially pointing at [.
  885. *)
  886.  
  887. LABEL 666, 777, 888, 999;
  888.  
  889. BEGIN
  890. WITH newTeXpage DO BEGIN
  891.    lastvalue := 0;
  892.    WHILE TRUE DO BEGIN
  893.       commpos := commpos + 1;
  894.       present[lastvalue] := GetInteger(commstring, commlen, commpos,
  895.                                        value[lastvalue]);
  896.       (* commpos now at commlen, space, period, non-digit or ']' *)
  897.       WHILE commpos < commlen DO BEGIN
  898.          IF commstring[commpos] <> ' ' THEN goto 888;
  899.          commpos := commpos + 1;   (* skip any spaces *)
  900.       END;
  901.       888:
  902.       IF commpos = commlen THEN BEGIN           (* check this first! *)
  903.          ClearMessageLine;
  904.          WriteString('] expected!');
  905.          BadCommandMessage;                     (* commpos at commlen *)
  906.          ParseTeXPage := FALSE;
  907.          goto 999;
  908.       END;
  909.       IF commstring[commpos] = ']' THEN BEGIN   (* end of TeX page spec *)
  910.          commpos := commpos + 1;
  911.          goto 777;
  912.       END;
  913.       IF lastvalue < 9 THEN
  914.          lastvalue := lastvalue + 1
  915.       ELSE BEGIN
  916.          ClearMessageLine;
  917.          WriteString('] expected after 10 integers!');
  918.          commpos := commpos + 1;
  919.          BadCommandMessage;
  920.          ParseTeXPage := FALSE;
  921.          goto 999;
  922.       END;
  923.       IF commstring[commpos] <> '.' THEN BEGIN
  924.          ClearMessageLine;
  925.          WriteString('Period, integer or ] expected!');
  926.          commpos := commpos + 1;
  927.          BadCommandMessage;
  928.          ParseTeXPage := FALSE;
  929.          goto 999;
  930.       END;
  931.    END;
  932.    777:
  933.    WHILE lastvalue > 0 DO BEGIN
  934.       IF present[lastvalue] THEN goto 666;
  935.       lastvalue := lastvalue - 1;
  936.    END;
  937.    666:
  938. END;
  939. ParseTeXPage := TRUE;
  940. 999:
  941. END; (* ParseTeXpage *)
  942.  
  943. (******************************************************************************)
  944.  
  945. FUNCTION TeXPageFound : BOOLEAN;
  946.  
  947. (* Return TRUE iff TeX page specification is valid and exists.
  948.    If so then position to lowest matching page.
  949. *)
  950.  
  951. VAR newTeXpage : TeXpageinfo;
  952.  
  953. BEGIN
  954. IF ParseTeXpage(newTeXpage) THEN
  955.    IF MoveToTeXPage(newTeXpage) THEN
  956.       TeXPageFound := TRUE            (* we found lowest matching page *)
  957.    ELSE BEGIN
  958.       ClearMessageLine;
  959.       WriteString('No TeX page matches your request!');
  960.       BadCommandMessage;
  961.       TeXPageFound := FALSE;
  962.    END
  963. ELSE
  964.    TeXPageFound := FALSE;             (* invalid TeX page specification *)
  965. END; (* TeXPageFound *)
  966.  
  967. (******************************************************************************)
  968.  
  969. FUNCTION Min (a, b : INTEGER) : INTEGER;
  970.  
  971. (* Return the minimum value of a and b. *)
  972.  
  973. BEGIN
  974. IF a < b THEN Min := a ELSE Min := b;
  975. END; (* Min *)
  976.  
  977. (******************************************************************************)
  978.  
  979. FUNCTION Max (a, b : INTEGER) : INTEGER;
  980.  
  981. (* Return the maximum value of a and b. *)
  982.  
  983. BEGIN
  984. IF a > b THEN Max := a ELSE Max := b;
  985. END; (* Max *)
  986.  
  987. (******************************************************************************)
  988.  
  989. PROCEDURE ProcessPage;
  990.  
  991. (* We are ready to interpret the current DVI page and fill in the various data
  992.    structures imported from DVIReader.  This routine will also:
  993.    set the window size and location to useful values (if autoviewing),
  994.    update pageoffpaper (after checking to see if it was TRUE for the previous
  995.    page processed as part of a multiple command string),
  996.    set screenjustcleared, paintwindow and paintWindowStatus to TRUE,
  997.    set paintDVIStatus to FALSE.
  998. *)
  999.  
  1000. VAR halfht, halfwd : INTEGER;
  1001.  
  1002. BEGIN
  1003. (* We check pageoffpaper here so user can type "NNNNNNNNNNNNN..." and note ALL
  1004.    the pages that are off the paper, not just the last one processed.
  1005. *)
  1006. IF pageoffpaper THEN BEGIN
  1007.    ClearMessageLine;
  1008.    WriteString('Page off paper!');   (* the previous page *)
  1009.    WaitForReturn;
  1010. END;
  1011. ClearScreen;
  1012. screenjustcleared := TRUE;
  1013. UpdateDVIStatusLine;       (* a MoveTo... routine has updated currDVI/TeXpage *)
  1014. paintDVIStatus := FALSE;
  1015. InterpretPage;             (* fill in DVIReader's page data structures *)
  1016. SortFonts(unusedfont);     (* sort fonts in order of least chars and return
  1017.                               pointer to first unused font *)
  1018. ClearMessageLine;          (* clear any message *)
  1019. IF pageempty THEN BEGIN
  1020.    minhp := 0; maxhp := 0; minvp := 0; maxvp := 0;   (* for window status *)
  1021. END;
  1022.  
  1023. IF autoviewing THEN BEGIN
  1024.    (* view as much of paper as possible but without too much distortion *)
  1025.    IF ((paperwd < paperht) AND (windowwd >= windowht)) OR
  1026.       ((paperwd = paperht) AND (windowwd >  windowht)) THEN BEGIN
  1027.       halfht := paperht DIV 2;
  1028.       IF ODD(paperht) THEN halfht := halfht + 1;  (* ensure bottom visible *)
  1029.       NewWindowHeight(halfht);                    (* try top half of paper *)
  1030.       NewWindowWidth(paperwd);
  1031.       NewLocation(paperleft,papertop);            (* top left corner of paper *)
  1032.       IF (NOT pageempty) AND outsidepage THEN
  1033.          NewLocation(paperleft,papertop+halfht);  (* try moving down *)
  1034.    END
  1035.    ELSE IF ((paperwd > paperht) AND (windowwd <= windowht)) OR
  1036.            ((paperwd = paperht) AND (windowwd <  windowht)) THEN BEGIN
  1037.       halfwd := paperwd DIV 2;
  1038.       IF ODD(paperwd) THEN halfwd := halfwd + 1;  (* ensure right visible *)
  1039.       NewWindowHeight(paperht);
  1040.       NewWindowWidth(halfwd);                     (* try left half of paper *)
  1041.       NewLocation(paperleft,papertop);            (* top left corner of paper *)
  1042.       IF (NOT pageempty) AND outsidepage THEN
  1043.          NewLocation(paperleft+halfwd,papertop);  (* try moving right *)
  1044.    END
  1045.    ELSE BEGIN
  1046.       (* paper shape matches unscaled window shape *)
  1047.       NewWindowHeight(paperht);                   (* try all of paper *)
  1048.       NewWindowWidth(paperwd);
  1049.       NewLocation(paperleft,papertop);            (* top left corner of paper *)
  1050.    END;
  1051. END
  1052. ELSE BEGIN
  1053.    (* not autoviewing, so use current window location and size *)
  1054.    NewWindowHeight(scaledht);
  1055.    NewWindowWidth(scaledwd);
  1056.    NewLocation(windowleft,windowtop);
  1057. END;
  1058.  
  1059. (* check if part/all of page is off paper;
  1060.    if so, and autoviewing is enabled, then we set window size and location
  1061.    so user can just see ALL of paper AND ALL of page.
  1062. *)
  1063. pageoffpaper := (NOT pageempty) AND
  1064.                 ((minhp < paperleft)  OR (minvp < papertop) OR
  1065.                  (maxhp > paperright) OR (maxvp > paperbottom));
  1066. IF pageoffpaper AND autoviewing THEN BEGIN
  1067.    NewWindowHeight (Max(maxvp,paperbottom) - Min(minvp,papertop) + 1);
  1068.    NewWindowWidth (Max(maxhp,paperright) - Min(minhp,paperleft) + 1);
  1069.    NewLocation (Min(minhp,paperleft),Min(minvp,papertop));
  1070. END;
  1071. paintWindowStatus := TRUE;
  1072. paintwindow := TRUE;
  1073. END; (* ProcessPage *)
  1074.  
  1075. (******************************************************************************)
  1076.  
  1077. PROCEDURE ChangeUnits;
  1078.  
  1079. (* Parse the rest of an Ic, Cm, Mm or PcPtPx command.
  1080.    commpos is pointing to next position in commstring.
  1081. *)
  1082.  
  1083. VAR nextch : CHAR;
  1084.  
  1085. BEGIN
  1086. IF commpos < commlen THEN BEGIN
  1087.    nextch := Cap(commstring[commpos]);
  1088.    commpos := commpos + 1;
  1089. END
  1090. ELSE
  1091.    nextch := ' ';
  1092. IF      (command = Ic)     AND (nextch = 'N') THEN currentunits := inunits
  1093. ELSE IF (command = Cm)     AND (nextch = 'M') THEN currentunits := cmunits
  1094. ELSE IF (command = Mm)     AND (nextch = 'M') THEN currentunits := mmunits
  1095. ELSE IF (command = PcPtPx) AND (nextch = 'C') THEN currentunits := pcunits
  1096. ELSE IF (command = PcPtPx) AND (nextch = 'T') THEN currentunits := ptunits
  1097. ELSE IF (command = PcPtPx) AND (nextch = 'X') THEN currentunits := pxunits
  1098. ELSE BEGIN
  1099.    ClearMessageLine;
  1100.    WriteString('Unknown units!');
  1101.    WriteChar(' '); WriteChar(' '); WriteChar(' ');
  1102.    CASE command OF
  1103.       Ic     : WriteString('IN');
  1104.       Cm     : WriteString('CM');
  1105.       Mm     : WriteString('MM');
  1106.       PcPtPx : WriteString('PC, PT or PX')
  1107.    END;
  1108.    WriteString(' expected.');
  1109.    BadCommandMessage;
  1110. END;
  1111. END; (* ChangeUnits *)
  1112.  
  1113. (******************************************************************************)
  1114.  
  1115. FUNCTION UserHitsReturn (VAR linecount : INTEGER) : BOOLEAN;
  1116.  
  1117. (* Do a WriteLine and return TRUE iff linecount = bottoml-2 AND user hits CR.
  1118.    If linecount < bottoml-2 then return FALSE; if not, and user hits
  1119.    something other than CR, then prepare a new screen before returning FALSE.
  1120. *)
  1121.  
  1122. LABEL 999;
  1123.  
  1124. VAR ch : CHAR;
  1125.  
  1126. BEGIN
  1127. WriteLine;
  1128. IF linecount = bottoml-2 THEN BEGIN   (* prompt for next screen *)
  1129.    MoveToTextLine(bottoml);
  1130.    WriteString('Hit RETURN key to resume page display,');
  1131.    WriteString(' or any other key for more:');
  1132.    WriteBuffer;
  1133.    ReadChar(ch);
  1134.    IF ch = CR THEN BEGIN UserHitsReturn := TRUE; goto 999 END;
  1135.    ClearScreen;
  1136.    MoveToTextLine(1);
  1137.    linecount := 1;
  1138. END
  1139. ELSE
  1140.    linecount := linecount + 1;
  1141. UserHitsReturn := FALSE;
  1142. 999:
  1143. END; (* UserHitsReturn *)
  1144.  
  1145. (******************************************************************************)
  1146.  
  1147. PROCEDURE WriteUnits;
  1148.  
  1149. BEGIN
  1150. CASE currentunits OF
  1151.    inunits : WriteString('in');
  1152.    cmunits : WriteString('cm');
  1153.    mmunits : WriteString('mm');
  1154.    pcunits : WriteString('pc');
  1155.    ptunits : WriteString('pt');
  1156.    pxunits : WriteString('px')
  1157. END;
  1158. END; (* WriteUnits *)
  1159.  
  1160. (******************************************************************************)
  1161.  
  1162. PROCEDURE WritePtSize (scaledsize : INTEGER);
  1163.  
  1164. (* Show given font size (in DVI units) in terms of (possibly magnified) pts. *)
  1165.  
  1166. VAR realdim : REAL; fracpart : INTEGER;
  1167.  
  1168. BEGIN
  1169. WriteString(' at'); WriteChar(' ');
  1170. realdim := (scaledsize / 16#10000) * (mag / 1000.0);
  1171. (* show realdim to an accuracy of 1 decimal place *)
  1172. IF ABS(realdim) < 0.05 THEN
  1173.    WriteChar('0')
  1174. ELSE BEGIN
  1175.    IF realdim < 0.0 THEN BEGIN
  1176.       WriteChar('-');
  1177.       realdim := ABS(realdim);
  1178.    END;
  1179.    realdim := realdim + 0.05;     (* round up to 1 decimal place *)
  1180.    WriteInt(TRUNC(realdim));      (* whole part *)
  1181.    fracpart := TRUNC((realdim - TRUNC(realdim)) * 10.0);   (* 0..9 *)
  1182.    IF fracpart > 0 THEN BEGIN
  1183.       WriteChar('.');
  1184.       WriteInt(fracpart);
  1185.    END;
  1186. END;
  1187. WriteString('pt');
  1188. END; (* WritePtSize *)
  1189.  
  1190. (******************************************************************************)
  1191.  
  1192. PROCEDURE ShowStatistics;
  1193.  
  1194. (* Show option values and font/character/rule/special statistics.
  1195.    UserHitsReturn controls pagination and takes the place of WriteLine.
  1196. *)
  1197.  
  1198. LABEL 999;
  1199.  
  1200. VAR temp : specialinfoptr; linecount, fontcount : INTEGER; ch : CHAR;
  1201.  
  1202. BEGIN
  1203. ClearScreen;
  1204. MoveToTextLine(1);
  1205. linecount := 1;
  1206. WriteString('DVI file          ='); WriteChar(' '); WriteString(DVIname);
  1207. IF UserHitsReturn(linecount) THEN goto 999;
  1208. WriteString('VDU               ='); WriteChar(' '); WriteString(vdu);
  1209. IF UserHitsReturn(linecount) THEN goto 999;
  1210. WriteString('Resolution        ='); WriteChar(' '); WriteInt(resolution);
  1211. WriteString(' pixels per inch');
  1212. IF UserHitsReturn(linecount) THEN goto 999;
  1213. WriteString('Magnification     ='); WriteChar(' '); WriteInt(mag);
  1214. IF mag <> DVImag THEN BEGIN
  1215.    WriteString(' (DVI mag of');  WriteChar(' '); WriteInt(DVImag);
  1216.    WriteString(' was overridden)');
  1217. END
  1218. ELSE
  1219.    WriteString(' (DVI mag)');
  1220. IF UserHitsReturn(linecount) THEN goto 999;
  1221. WriteString('TFM directory     ='); WriteChar(' '); WriteString(tfmdir);
  1222. IF UserHitsReturn(linecount) THEN goto 999;
  1223. WriteString('PS font prefix    ='); WriteChar(' '); WriteString(psprefix);
  1224. IF UserHitsReturn(linecount) THEN goto 999;
  1225. WriteString('Font directory    ='); WriteChar(' '); WriteString(fontdir);
  1226. IF UserHitsReturn(linecount) THEN goto 999;
  1227. WriteString('Dummy font        ='); WriteChar(' '); WriteString(dummyfont);
  1228. IF UserHitsReturn(linecount) THEN goto 999;
  1229. WriteString('Help file         ='); WriteChar(' '); WriteString(helpname);
  1230. IF UserHitsReturn(linecount) THEN goto 999;
  1231. WriteString('Horizontal offset ='); WriteChar(' ');
  1232. WriteDimension(hoffset); WriteUnits;
  1233. IF UserHitsReturn(linecount) THEN goto 999;
  1234. WriteString('Vertical offset   ='); WriteChar(' ');
  1235. WriteDimension(voffset); WriteUnits;
  1236. IF UserHitsReturn(linecount) THEN goto 999;
  1237. WriteString('Paper wd by ht    ='); WriteChar(' ');
  1238. WriteDimension(paperwd); WriteUnits;
  1239. WriteString(' by'); WriteChar(' ');
  1240. WriteDimension(paperht); WriteUnits;
  1241. IF UserHitsReturn(linecount) THEN goto 999;
  1242. IF UserHitsReturn(linecount) THEN goto 999;
  1243. WriteString('Total fonts on ALL pages ='); WriteChar(' ');
  1244. WriteInt(totalfonts);
  1245. IF UserHitsReturn(linecount) THEN goto 999;
  1246. IF UserHitsReturn(linecount) THEN goto 999;
  1247. WriteString('Fonts: (if used on current page then total chars given)');
  1248. IF UserHitsReturn(linecount) THEN goto 999;
  1249. fontcount := 0;
  1250. thisfontinfo  := fontlist;
  1251. WHILE thisfontinfo <> NIL DO
  1252.    WITH thisfontinfo^ DO BEGIN
  1253.       IF fontspeclen = 0 THEN               (* need to build fontspec *)
  1254.          BuildFontSpec(thisfontinfo);       (* fontexists may become TRUE *)
  1255.       WriteString(fontspec);
  1256.       IF psfont THEN WritePtSize(scaledsize);
  1257.       IF NOT fontexists THEN
  1258.          WriteString(' does not exist!');   (* use dummyfont instead *)
  1259.       IF fontused THEN BEGIN
  1260.          fontcount := fontcount + 1;
  1261.          WriteString('   (total chars ='); WriteChar(' ');
  1262.          WriteInt(totalchars);   WriteChar(')');
  1263.       END;
  1264.       IF UserHitsReturn(linecount) THEN goto 999;
  1265.       thisfontinfo := nextfont;
  1266.    END;
  1267. IF currDVIpage = 0 THEN
  1268.    WriteString('You haven''t selected a page yet.')
  1269. ELSE BEGIN
  1270.    WriteString('Total fonts on current page ='); WriteChar(' ');
  1271.    WriteInt(fontcount);
  1272. END;
  1273. IF UserHitsReturn(linecount) THEN goto 999;
  1274. IF UserHitsReturn(linecount) THEN goto 999;
  1275. WriteString('Total rules on current page ='); WriteChar(' ');
  1276. WriteInt(totalrules);
  1277. IF speciallist <> NIL THEN BEGIN
  1278.    IF UserHitsReturn(linecount) THEN goto 999;
  1279.    IF UserHitsReturn(linecount) THEN goto 999;
  1280.    WriteString('\special commands on current page:');
  1281.    IF UserHitsReturn(linecount) THEN goto 999;
  1282.    temp := speciallist;
  1283.    WHILE temp <> NIL DO
  1284.       WITH temp^ DO BEGIN
  1285.          WriteString('At (');
  1286.          WriteDimension(hp); WriteChar(',');
  1287.          WriteDimension(vp); WriteString('):'); WriteChar(' ');
  1288.          WriteString(special);
  1289.          IF UserHitsReturn(linecount) THEN goto 999;
  1290.          temp := nextspecial;
  1291.       END;
  1292. END;
  1293. WriteLine;
  1294. WriteLine;
  1295. MoveToTextLine(bottoml);
  1296. WriteString('Hit RETURN key to resume page display:');
  1297. WriteBuffer;
  1298. REPEAT ReadChar(ch) UNTIL ch = CR;
  1299. 999:
  1300. END; (* ShowStatistics *)
  1301.  
  1302. (******************************************************************************)
  1303.  
  1304. PROCEDURE ShowHelp;
  1305.  
  1306. (* Help information is displayed in lines 1 to bottoml-2.
  1307.    We assume that bottoml is at least 3 and that VDU screen is at least
  1308.    maxline characters wide.
  1309. *)
  1310.  
  1311. LABEL 888;
  1312.  
  1313. CONST
  1314.    maxline  = 80;   (* SYSDEP: helpname should have <= maxline chars/line *)
  1315.    maxlinem = maxline - 1;
  1316.  
  1317. VAR
  1318.    helpfile : integer;
  1319.    outline : ARRAY [0..maxlinem] OF CHAR;
  1320.    i, lines, length, result : INTEGER;
  1321.    ch, answer : CHAR;
  1322.  
  1323. BEGIN
  1324. length := Len(helpname);
  1325. IF length < maxstring THEN helpname[length] := CHR(0); (* terminate with NULL *)
  1326. helpfile := open(helpname, O_RDONLY, 0);               (* read only *)
  1327. IF length < maxstring THEN helpname[length] := ' ';    (* restore space *)
  1328. IF helpfile < 0 THEN BEGIN
  1329.    ClearMessageLine;
  1330.    WriteString('Couldn''t open help file'); WriteChar(' ');
  1331.    WriteString(helpname); WriteChar('!');
  1332.    WaitForReturn;
  1333.    ClearMessageLine;
  1334. END
  1335. ELSE BEGIN
  1336.    ClearScreen;
  1337.    MoveToTextLine(1);
  1338.    lines := 0;
  1339.    WHILE TRUE DO BEGIN
  1340.       IF read(helpfile,ch,1) = 0 THEN BEGIN     (* SYSDEP: end of file *)
  1341.          ClearTextLine(bottoml);
  1342.          MoveToTextLine(bottoml);
  1343.          WriteString('Hit RETURN key to resume page display:');
  1344.          WriteBuffer;
  1345.          REPEAT ReadChar(answer) UNTIL answer = CR;
  1346.          goto 888;
  1347.       END
  1348.       ELSE IF lines >= (bottoml-2) THEN BEGIN   (* blank line before prompt *)
  1349.          ClearTextLine(bottoml);
  1350.          MoveToTextLine(bottoml);
  1351.          WriteString('Hit RETURN key to resume page display,');
  1352.          WriteString(' or any other key for more help:');
  1353.          WriteBuffer;
  1354.          ReadChar(answer);
  1355.          IF answer = CR THEN goto 888;
  1356.          ClearScreen;
  1357.          MoveToTextLine(1);
  1358.          lines := 0;                            (* reset line count *)
  1359.       END;
  1360.       outline := ' ';
  1361.       i := 0;
  1362.       WHILE ch <> CR DO BEGIN
  1363.          IF i < maxline THEN outline[i] := ch;
  1364.          (* SYSDEP: check if eof occurs before eoln *)
  1365.          IF read(helpfile,ch,1) = 0 THEN ch := CR;
  1366.          i := i + 1;
  1367.       END;
  1368.       WriteString(outline); WriteLine;
  1369.       lines := lines + 1;
  1370.    END;
  1371.    888:
  1372.    result := Close(helpfile);
  1373.    ClearScreen;
  1374.    screenjustcleared := TRUE;
  1375.    paintDVIStatus := TRUE;
  1376.    paintWindowStatus := TRUE;
  1377.    IF currDVIpage <> 0 THEN paintwindow := TRUE;
  1378. END;
  1379. END; (* ShowHelp *)
  1380.  
  1381. (******************************************************************************)
  1382.  
  1383. PROCEDURE DisplayPaperEdges;
  1384.  
  1385. (* Display visible outlines of the imaginary sheet of paper.
  1386.    Thickness of outlines = 1 screen pixel no matter what the h and v scaling.
  1387. *)
  1388.  
  1389. LABEL 999;
  1390.  
  1391. CONST
  1392.    edgepixel = '.';             (* black pixel for outlines on non-graphic VDUs;
  1393.                                    note that VDU TeXtoASCII['.'] := '.' *)
  1394.  
  1395. VAR
  1396.    top, bot, left, right,       (* visible edges of paper in paper pixels *)
  1397.    scaledtop, scaledleft,       (* scaled visible edges in screen pixels *)
  1398.    scaledbot, scaledright,
  1399.    scaledheight, scaledwidth    (* scaled width and height *)
  1400.    : INTEGER;
  1401.  
  1402. BEGIN
  1403. (* first check if any part of paper is visible *)
  1404. IF papertop    > windowbottom THEN goto 999;
  1405. IF paperbottom < windowtop    THEN goto 999;
  1406. IF paperleft   > windowright  THEN goto 999;
  1407. IF paperright  < windowleft   THEN goto 999;
  1408. (* part or all of paper is visible, so return visible region *)
  1409. top   := Max(papertop,windowtop);
  1410. bot   := Min(paperbottom,windowbottom);
  1411. left  := Max(paperleft,windowleft);
  1412. right := Min(paperright,windowright);
  1413. scaledtop  := ScaleVpos(top - windowtop) + windowv;
  1414. scaledleft := ScaleHpos(left - windowleft) + windowh;
  1415. IF vscalefactor > 1.0 THEN
  1416.    scaledbot    := ScaleVpos(bot + 1 - windowtop) - 1 + windowv
  1417. ELSE
  1418.    scaledbot    := ScaleVpos(bot - windowtop) + windowv;
  1419. IF hscalefactor > 1.0 THEN
  1420.    scaledright  := ScaleHpos(right + 1 - windowleft) - 1 + windowh
  1421. ELSE
  1422.    scaledright  := ScaleHpos(right - windowleft) + windowh;
  1423. scaledheight := scaledbot - scaledtop + 1;
  1424. scaledwidth  := scaledright - scaledleft + 1;
  1425. (* only show visible edges if they are also paper outlines *)
  1426. IF left = paperleft THEN
  1427.    ShowRectangle(scaledleft, scaledtop, 1, scaledheight, edgepixel);
  1428. IF bot = paperbottom THEN
  1429.    ShowRectangle(scaledleft, scaledbot, scaledwidth, 1, edgepixel);
  1430. IF top = papertop THEN
  1431.    ShowRectangle(scaledleft, scaledtop, scaledwidth, 1, edgepixel);
  1432. IF right = paperright THEN
  1433.    ShowRectangle(scaledright, scaledtop, 1, scaledheight, edgepixel);
  1434. 999:
  1435. END; (* DisplayPaperEdges *)
  1436.  
  1437. (******************************************************************************)
  1438.  
  1439. FUNCTION RectangleVisible (intop, inbot, inleft, inright : INTEGER;
  1440.                            VAR outtop, outbot, outleft, outright : INTEGER
  1441.                           ) : BOOLEAN;
  1442.  
  1443. (* Return TRUE iff part or all of given rectangle would be visible
  1444.    in the current window.  Iff so, then we also return the visible
  1445.    region; the input and possible output rectangles are defined by their
  1446.    top, bottom, left and right edges in paper pixel coordinates.
  1447. *)
  1448.  
  1449. BEGIN
  1450. IF allpagevisible THEN BEGIN   (* all of rectangle must be visible *)
  1451.    outtop := intop; outbot := inbot; outleft := inleft; outright := inright;
  1452.    RectangleVisible := TRUE;
  1453. END
  1454. ELSE IF intop   > windowbottom THEN RectangleVisible := FALSE
  1455. ELSE IF inbot   < windowtop THEN    RectangleVisible := FALSE
  1456. ELSE IF inleft  > windowright THEN  RectangleVisible := FALSE
  1457. ELSE IF inright < windowleft THEN   RectangleVisible := FALSE
  1458. ELSE BEGIN
  1459.    (* part or all of rectangle is visible, so return visible region *)
  1460.    outtop   := Max(intop,windowtop);
  1461.    outbot   := Min(inbot,windowbottom);
  1462.    outleft  := Max(inleft,windowleft);
  1463.    outright := Min(inright,windowright);
  1464.    RectangleVisible := TRUE;
  1465. END;
  1466. END; (* RectangleVisible *)
  1467.  
  1468. (******************************************************************************)
  1469.  
  1470. PROCEDURE DisplayRules;
  1471.  
  1472. (* Display all pixels in rules, regardless of current displaymode.
  1473.    Rules will be displayed in the same order as in the DVI page (essentially
  1474.    top-down and left-right) because of the way DVIReader builds a rulelist.
  1475. *)
  1476.  
  1477. LABEL 999;
  1478.  
  1479. CONST
  1480.    rulepixel = '*';             (* black pixel for rules on non-graphic VDUs;
  1481.                                    note that VDU sets TeXtoASCII['*'] := '*' *)
  1482.  
  1483. VAR
  1484.    top, bottom, left, right,    (* visible edges of rule *)
  1485.    scaledtop, scaledleft,       (* scaled visible edges *)
  1486.    scaledbot, scaledright,
  1487.    scaledwidth, scaledheight,   (* scaled width and height *)
  1488.    thisrule : INTEGER;
  1489.    keyhit : CHAR;               (* returned by BusyRead if TRUE *)
  1490.  
  1491. BEGIN
  1492. thisruleinfo := rulelist;
  1493. WHILE thisruleinfo <> NIL DO
  1494.    WITH thisruleinfo^ DO BEGIN
  1495.       thisrule := 0;
  1496.       WHILE thisrule < rulecount DO BEGIN
  1497.          WITH ruletable[thisrule] DO BEGIN
  1498.             (* check if any part of rule is visible *)
  1499.             (* vp,hp is bottom left corner of rule on page *)
  1500.             IF RectangleVisible
  1501.                   (vp-ht+1,vp,hp,hp+wd-1,   (* rule edges *)
  1502.                    top,bottom,left,right)   (* visible rectangle *)
  1503.                THEN BEGIN
  1504.                (* show all pixels in this rectangle *)
  1505.                scaledtop  := ScaleVpos(top - windowtop) + windowv;
  1506.                scaledleft := ScaleHpos(left - windowleft) + windowh;
  1507.                IF vscalefactor > 1.0 THEN
  1508.                   scaledbot   := ScaleVpos(bottom+1-windowtop) - 1 + windowv
  1509.                ELSE
  1510.                   scaledbot   := ScaleVpos(bottom-windowtop) + windowv;
  1511.                IF hscalefactor > 1.0 THEN
  1512.                   scaledright := ScaleHpos(right+1-windowleft) - 1 + windowh
  1513.                ELSE
  1514.                   scaledright := ScaleHpos(right-windowleft) + windowh;
  1515.                scaledheight := scaledbot - scaledtop + 1;
  1516.                scaledwidth  := scaledright - scaledleft + 1;
  1517.                ShowRectangle
  1518.                   (scaledleft,        (* h coord of top left cnr *)
  1519.                    scaledtop,         (* v coord of top left cnr *)
  1520.                    scaledwidth,
  1521.                    scaledheight,
  1522.                    rulepixel);
  1523.                (* check keyboard after every visible rule *)
  1524.                IF BusyRead(keyhit) THEN BEGIN
  1525.                   keyhit := Cap(keyhit);
  1526.                   IF (keyhit = Terse) AND (displaymode <> tersemode) THEN BEGIN
  1527.                      displaymode := tersemode;
  1528.                      StartText;
  1529.                      UpdateDVIStatusLine;
  1530.                      StartGraphics;
  1531.                   END
  1532.                   ELSE IF (keyhit = Box) AND (displaymode <> boxmode) THEN BEGIN
  1533.                      displaymode := boxmode;
  1534.                      StartText;
  1535.                      UpdateDVIStatusLine;
  1536.                      StartGraphics;
  1537.                   END
  1538.                   ELSE IF (keyhit = Full) AND
  1539.                           (displaymode <> fullmode) THEN BEGIN
  1540.                      displaymode := fullmode;
  1541.                      StartText;
  1542.                      UpdateDVIStatusLine;
  1543.                      StartGraphics;
  1544.                   END
  1545.                   ELSE IF keyhit = CR THEN BEGIN
  1546.                      useraborted := TRUE;   (* checked in DisplayPage *)
  1547.                      goto 999;
  1548.                   END;
  1549.                END;
  1550.             END;
  1551.          END;
  1552.          thisrule := thisrule + 1;
  1553.       END;
  1554.       thisruleinfo := nextrule;
  1555.    END;
  1556. 999:
  1557. END; (* DisplayRules *)
  1558.  
  1559. (******************************************************************************)
  1560.  
  1561. FUNCTION PixelVisible (hpos, vpos : INTEGER) : BOOLEAN;
  1562.  
  1563. (* Return TRUE iff given paper pixel would be visible in current window. *)
  1564.  
  1565. BEGIN
  1566. IF allpagevisible THEN PixelVisible := TRUE
  1567. ELSE IF vpos < windowtop THEN PixelVisible := FALSE
  1568. ELSE IF vpos > windowbottom THEN PixelVisible := FALSE
  1569. ELSE IF hpos < windowleft THEN PixelVisible := FALSE
  1570. ELSE IF hpos > windowright THEN PixelVisible := FALSE
  1571. ELSE PixelVisible := TRUE;
  1572. END; (* PixelVisible *)
  1573.  
  1574. (******************************************************************************)
  1575.  
  1576. PROCEDURE TerseChar;
  1577.  
  1578. (* Display a quick and nasty representation of character only if ref pt visible.
  1579.    Just how good the representation is depends on the capabilities of the VDU.
  1580.    We don't bother checking if glyph is actually all white or non-existent.
  1581. *)
  1582.  
  1583. BEGIN
  1584. WITH thisfontinfo^ DO
  1585. WITH thischarinfo^.chartable[thischar] DO
  1586.    IF PixelVisible(hp,vp) THEN   (* ref pt of char is visible *)
  1587.       ShowChar(ScaleHpos(hp - windowleft) + windowh,
  1588.                ScaleVpos(vp - windowtop) + windowv,
  1589.                CHR(code));
  1590. END; (* TerseChar *)
  1591.  
  1592. (******************************************************************************)
  1593.  
  1594. PROCEDURE BoxChar;
  1595.  
  1596. (* Display visible box outlines of glyph.
  1597.    Thickness of outlines = 1 screen pixel no matter what the h and v scaling.
  1598. *)
  1599.  
  1600. LABEL 999;
  1601.  
  1602. VAR
  1603.    vpmyo, hpmxo,                (* vp-yo, hp-xo: glyph's top and left edges *)
  1604.    top, bottom, left, right,    (* visible edges of glyph *)
  1605.    scaledtop, scaledleft,       (* scaled visible edges *)
  1606.    scaledbot, scaledright,
  1607.    scaledheight, scaledwidth    (* scaled width and height *)
  1608.       : INTEGER;
  1609.    ch : CHAR;
  1610.  
  1611. BEGIN
  1612. WITH thisfontinfo^ DO
  1613. WITH thischarinfo^.chartable[thischar] DO
  1614. WITH pixelptr^[code] DO BEGIN
  1615.    IF mapadr = 0 THEN goto 999;                (* glyph all white or absent *)
  1616.    (* check if any part of glyph is visible *)
  1617.    vpmyo := vp-yo;
  1618.    hpmxo := hp-xo;
  1619.    IF RectangleVisible
  1620.          (vpmyo, vpmyo+ht-1, hpmxo, hpmxo+wd-1,(* glyph edges *)
  1621.           top,bottom,left,right)               (* visible part *)
  1622.       THEN BEGIN
  1623.       scaledtop  := ScaleVpos(top - windowtop) + windowv;
  1624.       scaledleft := ScaleHpos(left - windowleft) + windowh;
  1625.       IF vscalefactor > 1.0 THEN
  1626.          scaledbot    := ScaleVpos(bottom + 1 - windowtop) - 1 + windowv
  1627.       ELSE
  1628.          scaledbot    := ScaleVpos(bottom - windowtop) + windowv;
  1629.       IF hscalefactor > 1.0 THEN
  1630.          scaledright  := ScaleHpos(right + 1 - windowleft) - 1 + windowh
  1631.       ELSE
  1632.          scaledright  := ScaleHpos(right - windowleft) + windowh;
  1633.       scaledheight := scaledbot - scaledtop + 1;
  1634.       scaledwidth  := scaledright - scaledleft + 1;
  1635.       (* Only show edges that are also glyph outlines!
  1636.          Following method reduces the number of ShowRectangle calls needed for
  1637.          very small boxes.
  1638.       *)
  1639.       ch := CHR(code);
  1640.       IF ((scaledheight < 3) AND (top = vpmyo) AND (bottom = vpmyo+ht-1)) OR
  1641.          ((scaledwidth < 3) AND (left = hpmxo) AND (right = hpmxo+wd-1)) THEN
  1642.          ShowRectangle(scaledleft, scaledtop, scaledwidth, scaledheight, ch)
  1643.       ELSE BEGIN
  1644.          IF left = hpmxo THEN
  1645.             ShowRectangle(scaledleft, scaledtop, 1, scaledheight, ch);
  1646.          IF bottom = vpmyo+ht-1 THEN
  1647.             ShowRectangle(scaledleft, scaledbot, scaledwidth, 1, ch);
  1648.          IF top = vpmyo THEN
  1649.             ShowRectangle(scaledleft, scaledtop, scaledwidth, 1, ch);
  1650.          IF right = hpmxo+wd-1 THEN
  1651.             ShowRectangle(scaledright, scaledtop, 1, scaledheight, ch);
  1652.       END;
  1653.    END;
  1654. END;
  1655. 999:
  1656. END; (* BoxChar *)
  1657.  
  1658. (******************************************************************************)
  1659.  
  1660. PROCEDURE FullCharPS;
  1661.  
  1662. (* Display filled rectangle approximating the extent of PostScript glyph. *)
  1663.  
  1664. LABEL 999;
  1665.  
  1666. VAR
  1667.    vpmyo, hpmxo,                (* vp-yo, hp-xo: glyph's top and left edges *)
  1668.    top, bottom, left, right,    (* visible edges of glyph *)
  1669.    scaledtop, scaledleft,       (* scaled visible edges *)
  1670.    scaledbot, scaledright,
  1671.    scaledheight, scaledwidth    (* scaled width and height *)
  1672.       : INTEGER;
  1673.  
  1674. BEGIN
  1675. WITH thisfontinfo^ DO
  1676. WITH thischarinfo^.chartable[thischar] DO
  1677. WITH pixelptr^[code] DO BEGIN
  1678.    IF mapadr = 0 THEN goto 999;                (* glyph all white or absent *)
  1679.    (* check if any part of glyph is visible *)
  1680.    vpmyo := vp-yo;
  1681.    hpmxo := hp-xo;
  1682.    IF RectangleVisible
  1683.          (vpmyo, vpmyo+ht-1, hpmxo, hpmxo+wd-1,(* glyph edges *)
  1684.           top,bottom,left,right)               (* visible part *)
  1685.       THEN BEGIN
  1686.       scaledtop  := ScaleVpos(top - windowtop) + windowv;
  1687.       scaledleft := ScaleHpos(left - windowleft) + windowh;
  1688.       IF vscalefactor > 1.0 THEN
  1689.          scaledbot   := ScaleVpos(bottom + 1 - windowtop) - 1 + windowv
  1690.       ELSE
  1691.          scaledbot   := ScaleVpos(bottom - windowtop) + windowv;
  1692.       IF hscalefactor > 1.0 THEN
  1693.          scaledright := ScaleHpos(right + 1 - windowleft) - 1 + windowh
  1694.       ELSE
  1695.          scaledright := ScaleHpos(right - windowleft) + windowh;
  1696.       scaledheight := scaledbot - scaledtop + 1;
  1697.       scaledwidth  := scaledright - scaledleft + 1;
  1698.       ShowRectangle(scaledleft, scaledtop,scaledwidth, scaledheight, CHR(code));
  1699.    END;
  1700. END;
  1701. 999:
  1702. END; (* FullCharPS *)
  1703.  
  1704. (******************************************************************************)
  1705.  
  1706. PROCEDURE NotFound (VAR fspec : string);
  1707.  
  1708. BEGIN
  1709. StartText;
  1710. ResetVDU;      (* do before message since it might erase screen! *)
  1711. WriteString('Couldn''t open font'); WriteChar(' ');
  1712. WriteString(fspec); WriteChar('!'); WriteLine;
  1713. RestoreTerminal; exit(1);
  1714. END; (* NotFound *)
  1715.  
  1716. (******************************************************************************)
  1717.  
  1718. PROCEDURE FullChar;
  1719.  
  1720. (* Display all pixels in a glyph using bitmap from font file.
  1721.    The algorithm avoids overlapping rows when vscalefactor < 1.0.
  1722.    When hscalefactor < 1.0, it is not worth the extra code to avoid overlapping
  1723.    runs of 1 bits because the majority of character glyphs have only one or two
  1724.    runs per row.
  1725. *)
  1726.  
  1727. LABEL 666, 777, 888, 999;
  1728.  
  1729. CONST
  1730.    maxviswords = 100;
  1731.    maxviswordsm = maxviswords - 1;
  1732.    (* 100 * 32 = 3200 bits = maximum pixel width of glyph!
  1733.       If any fonts have glyphs wider than this then increase maxviswords.
  1734.    *)
  1735.  
  1736. TYPE
  1737.    (* SYSDEP: BITSET is 32 bit word with elements 31,30,29,...,0 *)
  1738.    glyphrow = ARRAY [0..maxviswordsm] OF BITSET;
  1739.  
  1740. VAR
  1741.    vpmyo, hpmxo,               (* vp-yo, hp-xo: glyph's top and left edges *)
  1742.    top, bottom, left, right,   (* visible edges of glyph *)
  1743.    scaledv, scalednextv,       (* scaled vertical positions for rows *)
  1744.    scaledh,                    (* scaled horizontal positions within row *)
  1745.    scaledwidth, scaledheight,  (* scaled width and height of row *)
  1746.    thisrow, thisbit,           (* in paper coordinates *)
  1747.    wordsperrow,                (* rows of bitmap are word aligned *)
  1748.    firstbit, lastbit,          (* somewhere in 0 .. wordsperrow*32-1 *)
  1749.    firstword, lastword,        (* somewhere in 0 .. wordsperrow-1 *)
  1750.    endword,                    (* = visible words in row, - 1 *)
  1751.    wordpos,                    (* 0 .. endword *)
  1752.    bitpos,                     (* 31 .. 0 *)
  1753.    i : INTEGER;
  1754.    row : glyphrow;             (* holds VISIBLE bits in one row of glyph;
  1755.                                   possibly > one row if vscalefactor < 1.0 *)
  1756.    ptr : int_or_bptr;          (* pointer into bitmap *)
  1757.    inrun : BOOLEAN;            (* are we in a run of black pixels in row? *)
  1758.  
  1759. BEGIN
  1760. WITH thisfontinfo^ DO
  1761. WITH thischarinfo^.chartable[thischar] DO
  1762. WITH pixelptr^[code] DO BEGIN
  1763.    IF mapadr = 0 THEN goto 999;                (* glyph all white or absent *)
  1764.    (* check if any part of glyph is visible *)
  1765.    vpmyo := vp-yo;
  1766.    hpmxo := hp-xo;
  1767.    IF RectangleVisible
  1768.          (vpmyo,vpmyo+ht-1,hpmxo,hpmxo+wd-1,   (* glyph edges *)
  1769.           top,bottom,left,right)               (* visible part *)
  1770.       THEN BEGIN
  1771.       IF bitmap.mptr = NIL THEN BEGIN
  1772.          IF NOT fontopen THEN BEGIN
  1773.             IF fontexists THEN BEGIN
  1774.                IF NOT OpenFontFile(fontspec) THEN NotFound(fontspec);
  1775.             END
  1776.             ELSE
  1777.                IF NOT OpenFontFile(dummyfont) THEN NotFound(dummyfont);
  1778.             fontopen := TRUE;                  (* only open font once *)
  1779.          END;
  1780.          GetBitmap(ht, wd,                     (* dimensions of bitmap *)
  1781.                    mapadr,                     (* bitmap info in font file *)
  1782.                    bitmap);                    (* starting address of bitmap *)
  1783.       END;
  1784.       wordsperrow := (wd + 31) DIV 32;         (* words in one row of bitmap *)
  1785.       firstbit    := left-hpmxo;               (* first visible bit *)
  1786.       lastbit     := right-hpmxo;              (* last visible bit *)
  1787.       firstword   := firstbit DIV 32;          (* first visible word *)
  1788.       lastword    := lastbit DIV 32;           (* last visible word *)
  1789.       endword     := lastword - firstword;
  1790.  
  1791.       (* we impose a limit on width of glyph (unlikely to be exceeded) *)
  1792.       { DEBUG
  1793.          IF endword > maxviswordsm THEN BEGIN
  1794.             StartText;
  1795.             ClearMessageLine;
  1796.             WriteString('Glyph'); WriteChar(' '); WriteInt(code);
  1797.             WriteString(' too wide!');
  1798.             WaitForReturn;
  1799.             StartGraphics;
  1800.          END;
  1801.       GUBED }
  1802.  
  1803.       (* set the visible words in row to 0 *)
  1804.       FOR i := 0 TO endword DO row[i] := [];
  1805.       (* calculate scaled v coord of first visible row *)
  1806.       scaledv := ScaleVpos(top - windowtop) + windowv;
  1807.  
  1808.       (* only consider visible rows; thisrow := top to bottom *)
  1809.       thisrow := top;
  1810.       WHILE TRUE DO BEGIN
  1811.          (* move to first byte of first visible word in this row *)
  1812.          ptr.int := bitmap.int +
  1813.                     4 * ((thisrow - vpmyo) * wordsperrow + firstword);
  1814.          (* get row of visible words from bitmap and OR with row array *)
  1815.          wordpos := 0;
  1816.          WHILE TRUE DO BEGIN
  1817.             row[wordpos] := ptr.bptr^ + row[wordpos];      (* set union *)
  1818.             IF wordpos = endword THEN goto 888;
  1819.             wordpos := wordpos + 1;
  1820.             ptr.int := ptr.int + 4;                        (* next word *)
  1821.          END;
  1822.          888:
  1823.          (* calculate scaled v coord of next row *)
  1824.          scalednextv  := ScaleVpos(thisrow + 1 - windowtop) + windowv;
  1825.          scaledheight := scalednextv - scaledv;
  1826.          IF (scaledheight > 0) OR (thisrow = bottom) THEN BEGIN
  1827.             (* display black pixels in row, doing any h/v expansion *)
  1828.             IF scaledheight < 1 THEN scaledheight := 1;    (* avoid 0 *)
  1829.             inrun := FALSE;
  1830.             bitpos := 31 - (firstbit MOD 32);   (* 31..0 *)
  1831.             wordpos := 0;
  1832.  
  1833.             (* only consider visible bits; thisbit := left to right *)
  1834.             thisbit := left;
  1835.             WHILE TRUE DO BEGIN
  1836.                IF bitpos IN row[wordpos] THEN BEGIN  (* start/continue run *)
  1837.                   IF NOT inrun THEN BEGIN            (* remember start of run *)
  1838.                      inrun := TRUE;
  1839.                      scaledh := ScaleHpos(thisbit - windowleft) + windowh;
  1840.                   END;
  1841.                END
  1842.                ELSE IF inrun THEN BEGIN      (* 0 bit has ended run *)
  1843.                   inrun := FALSE;
  1844.                   scaledwidth := ScaleHpos(thisbit - windowleft) + windowh
  1845.                                  - scaledh;
  1846.                   IF scaledwidth < 1 THEN scaledwidth := 1;   (* avoid 0 *)
  1847.                   ShowRectangle(scaledh,scaledv,
  1848.                                 scaledwidth,scaledheight,CHR(code));
  1849.                END;
  1850.                IF thisbit = right THEN goto 777; (* exit bit loop *)
  1851.                IF bitpos = 0 THEN BEGIN
  1852.                   wordpos := wordpos + 1;
  1853.                   bitpos := 31;
  1854.                END
  1855.                ELSE                          (* look at next bit in word *)
  1856.                   bitpos := bitpos - 1;
  1857.                thisbit := thisbit + 1;
  1858.             END; (* bit loop *)
  1859.             777:
  1860.             IF inrun THEN BEGIN              (* show run at end of row *)
  1861.                scaledwidth := ScaleHpos(thisbit + 1 - windowleft) + windowh
  1862.                               - scaledh;
  1863.                IF scaledwidth < 1 THEN scaledwidth := 1;   (* avoid 0 *)
  1864.                ShowRectangle(scaledh,scaledv,
  1865.                              scaledwidth,scaledheight,CHR(code));
  1866.             END;
  1867.  
  1868.             IF thisrow = bottom THEN goto 666;   (* exit row loop *)
  1869.             (* else reset the visible words in row to 0 *)
  1870.             FOR i := 0 TO endword DO row[i] := [];
  1871.          END;
  1872.          scaledv := scalednextv;
  1873.          thisrow := thisrow + 1;
  1874.       END; (* row loop *)
  1875.       666:
  1876.  
  1877.    END;
  1878. END;
  1879. 999:
  1880. END; (* FullChar *)
  1881.  
  1882. (******************************************************************************)
  1883.  
  1884. PROCEDURE DisplayChars;
  1885.  
  1886. (* Display all characters on a font by font basis.  How characters will be
  1887.    represented depends on the current displaymode (which the user can change
  1888.    while the window is being updated by typing the Tesre/Box/Full commands).
  1889.    Fonts will be displayed in order of ascending totalchars (due to SortFonts).
  1890.    Characters in a font will be displayed in a top-down, left-right manner
  1891.    because of the way DVIReader builds a charlist.
  1892. *)
  1893.  
  1894. LABEL 999;
  1895.  
  1896. VAR keyhit : CHAR;   (* check for abort or mode change *)
  1897.  
  1898. BEGIN
  1899. thisfontinfo := fontlist;
  1900. WHILE thisfontinfo <> unusedfont DO
  1901.    (* SortFont makes sure we only consider used fonts *)
  1902.    WITH thisfontinfo^ DO BEGIN
  1903.       fontopen := FALSE;                    (* might be set in FullChar *)
  1904.  
  1905.       (* Some VDUs may be able to simulate the given font.
  1906.          To help the VDU select appropriately sized characters, we need to
  1907.          pass the scaledsize of the font (converted to unscaled paper pixels),
  1908.          the overall mag, and the current h/vscalefactors.
  1909.       *)
  1910.       LoadFont(fontspec,
  1911.                PixelRound(scaledsize),
  1912.                mag/1000.0,
  1913.                hscalefactor,
  1914.                vscalefactor);
  1915.  
  1916.       thischarinfo := charlist;
  1917.       WHILE thischarinfo <> NIL DO BEGIN    (* display chars in chartable *)
  1918.          WITH thischarinfo^ DO BEGIN
  1919.             thischar := 0;
  1920.             WHILE thischar < charcount DO BEGIN
  1921.                IF displaymode = fullmode THEN BEGIN
  1922.                   IF psfont THEN FullCharPS ELSE FullChar
  1923.                END
  1924.                ELSE IF displaymode = tersemode THEN
  1925.                   TerseChar
  1926.                ELSE
  1927.                   BoxChar;
  1928.                (* check for abort or mode change *)
  1929.                IF BusyRead(keyhit) THEN BEGIN
  1930.                   keyhit := Cap(keyhit);
  1931.                   IF (keyhit = Terse) AND (displaymode <> tersemode) THEN BEGIN
  1932.                      displaymode := tersemode;
  1933.                      StartText;
  1934.                      UpdateDVIStatusLine;
  1935.                      StartGraphics;
  1936.                   END
  1937.                   ELSE IF (keyhit = Box) AND (displaymode <> boxmode) THEN BEGIN
  1938.                      displaymode := boxmode;
  1939.                      StartText;
  1940.                      UpdateDVIStatusLine;
  1941.                      StartGraphics;
  1942.                   END
  1943.                   ELSE IF (keyhit = Full) AND
  1944.                           (displaymode <> fullmode) THEN BEGIN
  1945.                      displaymode := fullmode;
  1946.                      StartText;
  1947.                      UpdateDVIStatusLine;
  1948.                      StartGraphics;
  1949.                   END
  1950.                   ELSE IF keyhit = CR THEN BEGIN
  1951.                      IF fontopen THEN CloseFontFile;
  1952.                      (* no need to set useraborted; DisplayRules done first *)
  1953.                      goto 999;
  1954.                   END;
  1955.                END;
  1956.                thischar := thischar + 1;
  1957.             END;
  1958.             thischarinfo := nextchar;
  1959.          END;
  1960.       END;
  1961.       IF fontopen THEN CloseFontFile;
  1962.       thisfontinfo := nextfont;
  1963.    END;
  1964. 999:
  1965. END; (* DisplayChars *)
  1966.  
  1967. (******************************************************************************)
  1968.  
  1969. PROCEDURE PaperMessage;
  1970.  
  1971. (* Called by CheckPageEdges to remind user of the paper size. *)
  1972.  
  1973. BEGIN
  1974. CASE currentunits OF
  1975.    inunits : WriteString('in');
  1976.    cmunits : WriteString('cm');
  1977.    mmunits : WriteString('mm');
  1978.    pcunits : WriteString('pc');
  1979.    ptunits : WriteString('pt');
  1980.    pxunits : WriteString('px')
  1981. END;
  1982. WriteString('!   (Paper is'); WriteChar(' ');
  1983. WriteDimension(paperwd); WriteString(' by'); WriteChar(' ');
  1984. WriteDimension(paperht); WriteChar(')');
  1985. WaitForReturn;
  1986. ClearMessageLine;
  1987. END; (* PaperMessage *)
  1988.  
  1989. (******************************************************************************)
  1990.  
  1991. PROCEDURE CheckPageEdges;
  1992.  
  1993. (* One or more page edges do not fall within the paper edges.
  1994.    This routine is called after the page & paper have been displayed so
  1995.    user can see how bad the problem is.
  1996. *)
  1997.  
  1998. BEGIN
  1999. IF minhp < paperleft THEN BEGIN
  2000.    ClearMessageLine;
  2001.    WriteString('Page beyond left edge by'); WriteChar(' ');
  2002.    WriteDimension(paperleft - minhp);
  2003.    PaperMessage;
  2004. END;
  2005. IF maxhp > paperright THEN BEGIN
  2006.    ClearMessageLine;
  2007.    WriteString('Page beyond right edge by'); WriteChar(' ');
  2008.    WriteDimension(maxhp - paperright);
  2009.    PaperMessage;
  2010. END;
  2011. IF minvp < papertop THEN BEGIN
  2012.    ClearMessageLine;
  2013.    WriteString('Page above top edge by'); WriteChar(' ');
  2014.    WriteDimension(papertop - minvp);
  2015.    PaperMessage;
  2016. END;
  2017. IF maxvp > paperbottom THEN BEGIN
  2018.    ClearMessageLine;
  2019.    WriteString('Page below bottom edge by'); WriteChar(' ');
  2020.    WriteDimension(maxvp - paperbottom);
  2021.    PaperMessage;
  2022. END;
  2023. END; (* CheckPageEdges *)
  2024.  
  2025. (******************************************************************************)
  2026.  
  2027. PROCEDURE DisplayPage;
  2028.  
  2029. (* Display page in window region based on window location and size,
  2030.    and displaymode.  This routine is only called if paintwindow is TRUE
  2031.    after all commands have been processed.
  2032. *)
  2033.  
  2034. BEGIN
  2035. IF screenjustcleared THEN BEGIN   (* avoid doing it again *)
  2036.    IF paintDVIStatus THEN UpdateDVIStatusLine;
  2037.    IF paintWindowStatus THEN UpdateWindowStatusLine;
  2038. END
  2039. ELSE BEGIN
  2040.    ClearScreen;
  2041.    UpdateDVIStatusLine;
  2042.    UpdateWindowStatusLine;
  2043. END;
  2044. StartGraphics;
  2045. DisplayPaperEdges;
  2046. StartText;
  2047. IF pageempty THEN BEGIN
  2048.    ClearMessageLine;
  2049.    WriteString('Page is empty.');
  2050. END
  2051. ELSE IF outsidepage THEN BEGIN
  2052.    IF pageoffpaper THEN CheckPageEdges;
  2053.    ClearMessageLine;
  2054.    WriteString('Window is'); WriteChar(' ');
  2055.    IF windowtop > maxvp THEN BEGIN
  2056.       WriteString('below'); WriteChar(' ');
  2057.       IF (windowleft > maxhp) OR (windowleft < minhp - scaledwd + 1) THEN BEGIN
  2058.          WriteString('and'); WriteChar(' ');
  2059.       END;
  2060.    END
  2061.    ELSE IF windowtop < minvp - scaledht + 1 THEN BEGIN
  2062.       WriteString('above'); WriteChar(' ');
  2063.       IF (windowleft > maxhp) OR (windowleft < minhp - scaledwd + 1) THEN BEGIN
  2064.          WriteString('and'); WriteChar(' ');
  2065.       END;
  2066.    END;
  2067.    IF windowleft > maxhp THEN BEGIN
  2068.       WriteString('to the right of'); WriteChar(' ');
  2069.    END
  2070.    ELSE IF windowleft < minhp - scaledwd + 1 THEN BEGIN
  2071.       WriteString('to the left of'); WriteChar(' ');
  2072.    END;
  2073.    WriteString('page.');
  2074. END
  2075. ELSE BEGIN
  2076.    (* Page is not empty and part or all of it is visible. *)
  2077.    StartGraphics;
  2078.    useraborted := FALSE;
  2079.    DisplayRules;
  2080.    IF NOT useraborted THEN DisplayChars;
  2081.    StartText;
  2082.    IF pageoffpaper THEN CheckPageEdges;
  2083.    IF allpagevisible THEN BEGIN
  2084.       ClearMessageLine;
  2085.       WriteString('Entire page is visible.');
  2086.    END;
  2087. END;
  2088. END; (* DisplayPage *)
  2089.  
  2090. (******************************************************************************)
  2091.  
  2092. PROCEDURE NextCommandLine;
  2093.  
  2094. (* Prompt user for next command line, parse response and call the
  2095.    appropriate command handler for each command in the line.
  2096. *)
  2097.  
  2098. LABEL 888, 999;
  2099.  
  2100. VAR n : INTEGER;             (* returned by GetInteger call *)
  2101.  
  2102. BEGIN
  2103. ClearTextLine(commandl);
  2104. MoveToTextLine(commandl);
  2105. WriteString(commprompt);
  2106. WriteBuffer;
  2107. ReadString(commstring);      (* read new command line *)
  2108. ClearMessageLine;            (* erase message line at this stage *)
  2109. commlen := maxstring;
  2110. commpos := 0;
  2111. WHILE commlen > 0 DO BEGIN
  2112.    IF commstring[commlen-1] <> ' ' THEN goto 888;
  2113.    commlen := commlen - 1;   (* ignore any trailing spaces *)
  2114. END;
  2115. 888:
  2116. (* initialize flags for multiple command processing *)
  2117. badcommand        := FALSE;
  2118. paintWindowStatus := FALSE;
  2119. paintDVIStatus    := FALSE;
  2120. paintwindow       := FALSE;
  2121. screenjustcleared := FALSE;
  2122. pageoffpaper      := FALSE;
  2123. WHILE (commpos < commlen) AND (NOT badcommand) DO BEGIN
  2124.    (* next command is defined by the next non-space character in commstring *)
  2125.    WHILE commstring[commpos] = ' ' DO BEGIN
  2126.       commpos := commpos + 1;   (* ignore any spaces *)
  2127.    END;
  2128.    command := Cap(commstring[commpos]);
  2129.    CASE command OF
  2130.       Window    : BEGIN
  2131.                   commpos := commpos + 1;
  2132.                   WindowMove;
  2133.                   IF NOT badcommand THEN paintWindowStatus := TRUE;
  2134.                   END;
  2135.       Up,
  2136.       Down      : BEGIN
  2137.                   commpos := commpos + 1;
  2138.                   WindowUpDown;
  2139.                   paintWindowStatus := TRUE;
  2140.                   END;
  2141.       Left,
  2142.       Right     : BEGIN
  2143.                   commpos := commpos + 1;
  2144.                   WindowLeftRight;
  2145.                   paintWindowStatus := TRUE;
  2146.                   END;
  2147.       Hsize     : BEGIN
  2148.                   commpos := commpos + 1;
  2149.                   SetWindowWidth;
  2150.                   NewLocation(windowleft,windowtop);
  2151.                   paintWindowStatus := TRUE;
  2152.                   END;
  2153.       Vsize     : BEGIN
  2154.                   commpos := commpos + 1;
  2155.                   SetWindowHeight;
  2156.                   NewLocation(windowleft,windowtop);
  2157.                   paintWindowStatus := TRUE;
  2158.                   END;
  2159.       AutoView  : BEGIN
  2160.                   commpos := commpos + 1;
  2161.                   SetAutoView;
  2162.                   IF NOT badcommand THEN paintDVIStatus := TRUE;
  2163.                   END;
  2164.       ZoomInOut : BEGIN
  2165.                   commpos := commpos + 1;
  2166.                   ZoomWindow;
  2167.                   IF NOT badcommand THEN NewLocation(windowleft,windowtop);
  2168.                   IF NOT badcommand THEN paintWindowStatus := TRUE;
  2169.                   END;
  2170.       NextPage  : BEGIN
  2171.                   commpos := commpos + 1;
  2172.                   IF NextPageFound THEN ProcessPage;
  2173.                   END;
  2174.       '0','1','2','3','4','5','6','7','8','9'  :
  2175.                   IF GetInteger(commstring,commlen,commpos,n) THEN
  2176.                      (* must be true, and commpos now after last digit *)
  2177.                      IF DVIPageFound(n) THEN
  2178.                         ProcessPage;
  2179.       TeXpage   : IF TeXPageFound THEN ProcessPage;
  2180.                   (* commpos incremented in ParseTeXpage *)
  2181.       Forwards  : BEGIN
  2182.                   commpos := commpos + 1;
  2183.                   ascending := TRUE;
  2184.                   paintDVIStatus := TRUE;
  2185.                   END;
  2186.       Backwards : BEGIN
  2187.                   commpos := commpos + 1;
  2188.                   ascending := FALSE;
  2189.                   paintDVIStatus := TRUE;
  2190.                   END;
  2191.       Terse     : BEGIN
  2192.                   commpos := commpos + 1;
  2193.                   displaymode := tersemode;
  2194.                   paintDVIStatus := TRUE;
  2195.                   IF currDVIpage <> 0 THEN paintwindow := TRUE;
  2196.                   END;
  2197.       Box       : BEGIN
  2198.                   commpos := commpos + 1;
  2199.                   displaymode := boxmode;
  2200.                   paintDVIStatus := TRUE;
  2201.                   IF currDVIpage <> 0 THEN paintwindow := TRUE;
  2202.                   END;
  2203.       Full      : BEGIN
  2204.                   commpos := commpos + 1;
  2205.                   displaymode := fullmode;
  2206.                   paintDVIStatus := TRUE;
  2207.                   IF currDVIpage <> 0 THEN paintwindow := TRUE;
  2208.                   END;
  2209.       Ic, Cm, Mm,
  2210.       PcPtPx    : BEGIN
  2211.                   commpos := commpos + 1;
  2212.                   ChangeUnits;
  2213.                   IF NOT badcommand THEN paintWindowStatus := TRUE;
  2214.                   END;
  2215.       Help      : BEGIN
  2216.                   commpos := commpos + 1;
  2217.                   ShowHelp;
  2218.                   END;
  2219.       Show      : BEGIN
  2220.                   commpos := commpos + 1;
  2221.                   ShowStatistics;
  2222.                   ClearScreen;
  2223.                   screenjustcleared := TRUE;
  2224.                   paintDVIStatus := TRUE;
  2225.                   paintWindowStatus := TRUE;
  2226.                   IF currDVIpage <> 0 THEN paintwindow := TRUE;
  2227.                   END;
  2228.       Quit      : goto 999;
  2229.    OTHERWISE
  2230.       commpos := commpos + 1;
  2231.       ClearMessageLine;
  2232.       WriteString('Unknown command!   Type'); WriteChar(' ');
  2233.       WriteChar(Help); WriteString(' for help.');
  2234.       BadCommandMessage;
  2235.    END;
  2236. END;
  2237. IF paintwindow THEN
  2238.    DisplayPage        (* only update window after processing all commands *)
  2239. ELSE BEGIN
  2240.    IF paintDVIStatus THEN UpdateDVIStatusLine;
  2241.    IF paintWindowStatus THEN UpdateWindowStatusLine;
  2242. END;
  2243. 999:
  2244. END; (* NextCommandLine *)
  2245.  
  2246. (******************************************************************************)
  2247.  
  2248. PROCEDURE Finish;
  2249.  
  2250. BEGIN
  2251. CloseDVIFile;
  2252. ClearScreen;
  2253. MoveToTextLine(1);
  2254. WriteLine;
  2255. ResetVDU;
  2256. RestoreTerminal; exit(0);   (* halt with success *)
  2257. END; (* Finish *)
  2258.  
  2259. (******************************************************************************)
  2260.  
  2261. BEGIN
  2262. InitScreenIO;                          (* SYSDEP: sets cbreak on & echo off *)
  2263. InitOptions;                           (* init DVIname and command options *)
  2264. InitDVIReader;
  2265. InitFontReader;
  2266. OpenDVIFile(DVIname);                  (* and read DVImag etc. *)
  2267. IF mag = 0 THEN mag := DVImag;         (* use DVImag *)
  2268. SetConversionFactor(resolution,mag);   (* for DVIReader *)
  2269. InitVDU;                               (* init windowwd/ht etc. *)
  2270. Initialize;
  2271. StartText;
  2272. ClearScreen;
  2273. UpdateDVIStatusLine;
  2274. UpdateWindowStatusLine;
  2275. MoveToTextLine(messagel);
  2276. WriteString(version);
  2277. REPEAT
  2278.    NextCommandLine;                    (* parse and execute command(s) *)
  2279. UNTIL command = Quit;
  2280. Finish;
  2281. END. (* DVItoVDU *)
  2282.